diff options
Diffstat (limited to 'src/runtimerender')
180 files changed, 62019 insertions, 0 deletions
diff --git a/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp b/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp new file mode 100644 index 0000000..e945335 --- /dev/null +++ b/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** 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); + delete m_areaAllocator; +} + +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::setTextureData(qt3ds::render::NVRenderTexture2D *texture, + QImage &image) +{ + bool isGLES2 = m_context.GetRenderContext().GetRenderContextType() + == qt3ds::render::NVRenderContextValues::GLES2; + texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(), image.byteCount()), + 0, image.width(), image.height(), + isGLES2 ? qt3ds::render::NVRenderTextureFormats::Alpha8 + : qt3ds::render::NVRenderTextureFormats::R8); +} + +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); + setTextureData(info->texture, image); + } + + qt3ds::render::STextureDetails textureDetails = info->texture->GetTextureDetails(); + if (int(textureDetails.m_Width) != width || int(textureDetails.m_Height) != height) + setTextureData(info->texture, image); + + 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; + + setTextureData(info->texture, image); + } +} + +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; + + setTextureData(i.key()->texture, image); + } +} + +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; + setTextureData(texInfo->texture, image); + + 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/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp b/src/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp new file mode 100644 index 0000000..750301d --- /dev/null +++ b/src/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/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h b/src/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h new file mode 100644 index 0000000..19d3c08 --- /dev/null +++ b/src/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/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h b/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h new file mode 100644 index 0000000..679d3ad --- /dev/null +++ b/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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); + void setTextureData(qt3ds::render::NVRenderTexture2D *texture, QImage &image); + + 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/runtimerender/Qt3DSDistanceFieldRenderer.cpp b/src/runtimerender/Qt3DSDistanceFieldRenderer.cpp new file mode 100644 index 0000000..8c1b28a --- /dev/null +++ b/src/runtimerender/Qt3DSDistanceFieldRenderer.cpp @@ -0,0 +1,1067 @@ +/**************************************************************************** +** +** 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); + 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); + const float halfWidth = boundingBox.x() / 2.0f; + const float halfHeight = boundingBox.y() / 2.0f; + bool hasValidBoundingBox = boundingBox.x() > 0 || boundingBox.y() > 0; + + QRawFont font = m_fontDatabase.findFont(textInfo.m_Font.c_str()); + qreal scaleFactor = font.pixelSize() / qreal(textInfo.m_FontSize); + + const qreal maximumWidth = boundingBox.isNull() ? qreal(0x01000000) + : qreal(boundingBox.x()) * scaleFactor; + const qreal maximumHeight = boundingBox.isNull() ? qreal(0x01000000) + : qreal(boundingBox.y()) * scaleFactor; + + 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(); + + qreal leading = qreal(textInfo.m_Leading) * scaleFactor; + width = 0.0; + height = -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) * scaleFactor); + 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 += 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); + + cx1 /= float(scaleFactor); + cy1 /= float(scaleFactor); + cx2 /= float(scaleFactor); + cy2 /= float(scaleFactor); + + 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); + } + + float x1Clip = 1.0f; + float x2Clip = 1.0f; + float y1Clip = 1.0f; + float y2Clip = 1.0f; + + if (hasValidBoundingBox) { + if ((cx1 < -halfWidth && cx2 < -halfWidth) + || (cx1 > halfWidth && cx2 > halfWidth) + || (cy1 < -halfHeight && cy2 < -halfHeight) + || (cy1 > halfHeight && cy2 > halfHeight)) { + continue; + } + + float xDiff = qAbs(cx1 - cx2); + float yDiff = qAbs(cy1 - cy2); + + if (cx1 < -halfWidth) { + x1Clip = 1.0f - qAbs(cx1 - (-halfWidth)) / xDiff; + cx1 = -halfWidth; + } + + if (cx2 > halfWidth) { + x2Clip = 1.0f - qAbs(cx2 - halfWidth) / xDiff; + cx2 = halfWidth; + } + + if (cy1 < -halfHeight) { + y1Clip = 1.0f - qAbs(cy1 - (-halfHeight)) / yDiff; + cy1 = -halfHeight; + } + + if (cy2 > halfHeight) { + y2Clip = 1.0f - qAbs(cy2 - halfHeight) / yDiff; + cy2 = halfHeight; + } + } + + 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 (hasValidBoundingBox) { + 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); + } + + if (hasValidBoundingBox) { + float tx1Orig = tx1; + float tx2Orig = tx2; + float ty1Orig = ty1; + float ty2Orig = ty2; + + float xDiff = qAbs(tx1 - tx2); + tx1 = tx2Orig - xDiff * x1Clip; + tx2 = tx1Orig + xDiff * x2Clip; + + float yDiff = qAbs(ty1 - ty2); + ty1 = ty2Orig - yDiff * y1Clip; + ty2 = ty1Orig + yDiff * y2Clip; + } + + 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.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.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, + 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.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, + 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.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, text.m_TextColor.w); + 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); + 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) +{ + if (!m_shader.program) + buildShaders(); + + float alpha = text.m_GlobalOpacity * text.m_TextColor.w; + QT3DSVec4 textColor = QT3DSVec4(text.m_TextColor.getXYZ() * alpha, alpha); + int shadowRgb = int(100 - int(text.m_DropShadowStrength)); + QT3DSVec4 shadowColor(shadowRgb * 0.01f * alpha, shadowRgb * 0.01f * alpha, + shadowRgb * 0.01f * alpha, alpha); + + 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, + int(textureDetails.m_Width), int(textureDetails.m_Height), + glyphInfo.fontScale * float(m_pixelRatio), + QT3DSVec2(glyphInfo.shadowOffsetX, glyphInfo.shadowOffsetY), + textColor, shadowColor); + } else { + renderMesh(mesh.inputAssembler, it.key()->texture, mvp, + int(textureDetails.m_Width), int(textureDetails.m_Height), + glyphInfo.fontScale * float(m_pixelRatio), textColor); + } + + m_renderedGlyphs += glyphHashValue; + } + + text.m_Bounds = NVBounds3(minimum, maximum); +} + +void Q3DSDistanceFieldRenderer::renderTextDepth(SText &text, const QT3DSMat44 &mvp) +{ + // TODO: Create a depth pass shader for distance field text + renderText(text, mvp); +} + +void Q3DSDistanceFieldRenderer::setContext(IQt3DSRenderContext &context) +{ + m_context = &context; + m_glyphCacheManager.setContext(context); + buildShaders(); +} + +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(); +} + +bool Q3DSDistanceFieldRenderer::checkAndBuildGlyphs(SText &text) +{ + auto hashVal = getTextHashValue(text); + if (!m_glyphCache.contains(hashVal)) { + m_glyphCache[hashVal] = buildGlyphsPerTexture(text); + return true; + } + + return false; +} +#endif diff --git a/src/runtimerender/Qt3DSDistanceFieldRenderer.h b/src/runtimerender/Qt3DSDistanceFieldRenderer.h new file mode 100644 index 0000000..3af19a9 --- /dev/null +++ b/src/runtimerender/Qt3DSDistanceFieldRenderer.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** 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<QT3DSI32> textureWidth; + NVRenderCachedShaderProperty<QT3DSI32> textureHeight; + NVRenderCachedShaderProperty<QT3DSF32> fontScale; + NVRenderCachedShaderProperty<NVRenderTexture2D *> texture; + NVRenderCachedShaderProperty<QT3DSVec4> color; +}; + +struct Q3DSDistanceFieldDropShadowShader { + NVRenderShaderProgram *program = nullptr; + NVRenderCachedShaderProperty<QT3DSMat44> mvp; + 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, QT3DSI32 textureWidth, QT3DSI32 textureHeight, + QT3DSF32 fontScale, QT3DSVec4 color); + void renderMeshWithDropShadow(NVRenderInputAssembler *inputAssembler, + NVRenderTexture2D *texture, const QT3DSMat44 &mvp, + QT3DSI32 textureWidth, QT3DSI32 textureHeight, + QT3DSF32 fontScale, QT3DSVec2 shadowOffset, + QT3DSVec4 color, QT3DSVec4 shadowColor); + void renderText(SText &text, const QT3DSMat44 &mvp); + void renderTextDepth(SText &text, const QT3DSMat44 &mvp); + void setContext(IQt3DSRenderContext &context); + + bool checkAndBuildGlyphs(SText &text); + + 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/runtimerender/Qt3DSFontDatabase.cpp b/src/runtimerender/Qt3DSFontDatabase.cpp new file mode 100644 index 0000000..b44cdbd --- /dev/null +++ b/src/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/runtimerender/Qt3DSFontDatabase_p.h b/src/runtimerender/Qt3DSFontDatabase_p.h new file mode 100644 index 0000000..73e0362 --- /dev/null +++ b/src/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/runtimerender/Qt3DSOffscreenRenderKey.h b/src/runtimerender/Qt3DSOffscreenRenderKey.h new file mode 100644 index 0000000..555c268 --- /dev/null +++ b/src/runtimerender/Qt3DSOffscreenRenderKey.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_OFFSCREEN_RENDER_KEY_H +#define QT3DS_OFFSCREEN_RENDER_KEY_H +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSDiscriminatedUnion.h" + +namespace qt3ds { +namespace foundation { + + template <> + struct DestructTraits<CRegisteredString> + { + void destruct(CRegisteredString &) {} + }; +} +} + +namespace qt3ds { +namespace render { + struct OffscreenRendererKeyTypes + { + enum Enum { + NoOffscreenRendererKey = 0, + RegisteredString, + VoidPtr, + }; + }; + + template <typename TDType> + struct SOffscreenRendererKeyTypeMap + { + }; + template <> + struct SOffscreenRendererKeyTypeMap<CRegisteredString> + { + enum { KeyType = OffscreenRendererKeyTypes::RegisteredString }; + }; + template <> + struct SOffscreenRendererKeyTypeMap<void *> + { + enum { KeyType = OffscreenRendererKeyTypes::VoidPtr }; + }; + + struct SOffscreenRendererKeyUnionTraits + { + typedef OffscreenRendererKeyTypes::Enum TIdType; + enum { + TBufferSize = sizeof(CRegisteredString), + }; + + static TIdType getNoDataId() { return OffscreenRendererKeyTypes::NoOffscreenRendererKey; } + + template <typename TDataType> + static TIdType getType() + { + return (TIdType)SOffscreenRendererKeyTypeMap<TDataType>::KeyType; + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case OffscreenRendererKeyTypes::RegisteredString: + return inVisitor(*NVUnionCast<CRegisteredString *>(inData)); + case OffscreenRendererKeyTypes::VoidPtr: + return inVisitor(*NVUnionCast<void **>(inData)); + default: + QT3DS_ASSERT(false); + case OffscreenRendererKeyTypes::NoOffscreenRendererKey: + return inVisitor(); + } + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case OffscreenRendererKeyTypes::RegisteredString: + return inVisitor(*NVUnionCast<const CRegisteredString *>(inData)); + case OffscreenRendererKeyTypes::VoidPtr: + return inVisitor(*NVUnionCast<const void **>(inData)); + default: + QT3DS_ASSERT(false); + case OffscreenRendererKeyTypes::NoOffscreenRendererKey: + return inVisitor(); + } + } + }; + + typedef qt3ds::foundation:: + DiscriminatedUnion<qt3ds::foundation:: + DiscriminatedUnionGenericBase<SOffscreenRendererKeyUnionTraits, + SOffscreenRendererKeyUnionTraits:: + TBufferSize>, + SOffscreenRendererKeyUnionTraits::TBufferSize> + TOffscreenRendererKeyUnionType; + + struct SOffscreenRendererKey : public TOffscreenRendererKeyUnionType + { + typedef TOffscreenRendererKeyUnionType TBase; + SOffscreenRendererKey() {} + SOffscreenRendererKey(const CRegisteredString &str) + : TBase(str) + { + } + SOffscreenRendererKey(void *key) + : TBase(key) + { + } + SOffscreenRendererKey(const SOffscreenRendererKey &other) + : TBase(static_cast<const TBase &>(other)) + { + } + SOffscreenRendererKey &operator=(const SOffscreenRendererKey &other) + { + TBase::operator=(other); + return *this; + } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSOffscreenRenderManager.cpp b/src/runtimerender/Qt3DSOffscreenRenderManager.cpp new file mode 100644 index 0000000..7d66c7a --- /dev/null +++ b/src/runtimerender/Qt3DSOffscreenRenderManager.cpp @@ -0,0 +1,498 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSOffscreenRenderManager.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "foundation/FastAllocator.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderResourceTexture2D.h" +#include "Qt3DSRenderResourceBufferObjects.h" +#include "Qt3DSRendererUtil.h" + +using namespace qt3ds::render; + +namespace eastl { +template <> +struct hash<SOffscreenRendererKey> +{ + size_t operator()(const SOffscreenRendererKey &key) const + { + switch (key.getType()) { + case OffscreenRendererKeyTypes::RegisteredString: + return hash<CRegisteredString>()(key.getData<CRegisteredString>()); + case OffscreenRendererKeyTypes::VoidPtr: + return hash<size_t>()(reinterpret_cast<size_t>(key.getData<void *>())); + default: + break; + } + QT3DS_ASSERT(false); + return 0; + } + bool operator()(const SOffscreenRendererKey &lhs, const SOffscreenRendererKey &rhs) const + { + return lhs == rhs; + } +}; +} + +namespace { + +using eastl::pair; +using eastl::make_pair; + +struct SRendererData : SOffscreenRenderResult +{ + NVAllocatorCallback &m_Allocator; + IResourceManager &m_ResourceManager; + QT3DSU32 m_FrameCount; + bool m_Rendering; + + SRendererData(NVAllocatorCallback &inAllocator, IResourceManager &inResourceManager) + : m_Allocator(inAllocator) + , m_ResourceManager(inResourceManager) + , m_FrameCount(QT3DS_MAX_U32) + , m_Rendering(false) + { + } + ~SRendererData() + { + if (m_Texture) + m_ResourceManager.Release(*m_Texture); + m_Texture = NULL; + } + void Release() { NVDelete(m_Allocator, this); } +}; + +struct SScopedRenderDataRenderMarker +{ + SRendererData &m_Data; + SScopedRenderDataRenderMarker(SRendererData &d) + : m_Data(d) + { + QT3DS_ASSERT(m_Data.m_Rendering == false); + m_Data.m_Rendering = true; + } + ~SScopedRenderDataRenderMarker() { m_Data.m_Rendering = false; } +}; + +struct SRenderDataReleaser +{ + SRendererData *mPtr; + SRenderDataReleaser(SRendererData *inItem) + : mPtr(inItem) + { + } + // Transfer ownership + SRenderDataReleaser(const SRenderDataReleaser &inOther) + : mPtr(inOther.mPtr) + { + const_cast<SRenderDataReleaser &>(inOther).mPtr = NULL; + } + + ~SRenderDataReleaser() + { + if (mPtr) + mPtr->Release(); + } +}; +struct SOffscreenRenderManager; + +struct SOffscreenRunnable : public IRenderTask +{ + SOffscreenRenderManager &m_RenderManager; + SRendererData &m_Data; + SOffscreenRendererEnvironment m_DesiredEnvironment; + SOffscreenRunnable(SOffscreenRenderManager &rm, SRendererData &data, + SOffscreenRendererEnvironment env) + : m_RenderManager(rm) + , m_Data(data) + , m_DesiredEnvironment(env) + { + } + void Run() override; +}; + +struct SOffscreenRenderManager : public IOffscreenRenderManager +{ + typedef nvhash_map<SOffscreenRendererKey, SRenderDataReleaser> TRendererMap; + IQt3DSRenderContext &m_Context; + NVAllocatorCallback &m_Allocator; + NVScopedRefCounted<IStringTable> m_StringTable; + NVScopedRefCounted<IResourceManager> m_ResourceManager; + TRendererMap m_Renderers; + SFastAllocator<> m_PerFrameAllocator; + QT3DSU32 m_FrameCount; // cheap per- + + volatile QT3DSI32 mRefCount; + + SOffscreenRenderManager(NVAllocatorCallback &inCallback, IStringTable &inStringTable, + IResourceManager &inManager, IQt3DSRenderContext &inContext) + : m_Context(inContext) + , m_Allocator(inCallback) + , m_StringTable(inStringTable) + , m_ResourceManager(inManager) + , m_Renderers(inCallback, "SOffscreenRenderManager::m_Renderers") + , m_PerFrameAllocator(inCallback, "m_PerFrameAllocator") + , m_FrameCount(0) + , mRefCount(0) + { + } + + virtual ~SOffscreenRenderManager() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Allocator) + + Option<bool> MaybeRegisterOffscreenRenderer(const SOffscreenRendererKey &inKey, + IOffscreenRenderer &inRenderer) override + { + TRendererMap::iterator theIter = m_Renderers.find(inKey); + if (theIter != m_Renderers.end()) { + SRendererData &theData = *(theIter->second.mPtr); + if (theData.m_Renderer != &inRenderer) { + if (inKey.getType() == OffscreenRendererKeyTypes::RegisteredString) { + qCCritical(INVALID_OPERATION, "Different renderers registered under same key: %s", + inKey.getData<CRegisteredString>().c_str()); + } + QT3DS_ASSERT(false); + return Empty(); + } + return false; + } + RegisterOffscreenRenderer(inKey, inRenderer); + return true; + } + + void RegisterOffscreenRenderer(const SOffscreenRendererKey &inKey, + IOffscreenRenderer &inRenderer) override + { + pair<TRendererMap::iterator, bool> theInsert = m_Renderers.insert( + make_pair(inKey, QT3DS_NEW(m_Allocator, SRendererData)(m_Allocator, *m_ResourceManager))); + QT3DS_ASSERT(theInsert.second); + SRendererData &theData = *(theInsert.first->second.mPtr); + theData.m_Renderer = &inRenderer; + } + + bool HasOffscreenRenderer(const SOffscreenRendererKey &inKey) override + { + return m_Renderers.find(inKey) != m_Renderers.end(); + } + + IOffscreenRenderer *GetOffscreenRenderer(const SOffscreenRendererKey &inKey) override + { + TRendererMap::iterator theRenderer = m_Renderers.find(inKey); + if (theRenderer != m_Renderers.end()) { + SRendererData &theData = *theRenderer->second.mPtr; + return theData.m_Renderer; + } + return NULL; + } + void ReleaseOffscreenRenderer(const SOffscreenRendererKey &inKey) override + { + m_Renderers.erase(inKey); + } + + void RenderItem(SRendererData &theData, SOffscreenRendererEnvironment theDesiredEnvironment) + { + NVRenderContext &theContext = m_ResourceManager->GetRenderContext(); + QT3DSVec2 thePresScaleFactor = m_Context.GetPresentationScaleFactor(); + SOffscreenRendererEnvironment theOriginalDesiredEnvironment(theDesiredEnvironment); + // Ensure that our overall render context comes back no matter what the client does. + qt3ds::render::NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + QT3DSVec4(0, 0, 0, 0)); + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __scissorRect( + theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + qt3ds::render::NVRenderContextScopedProperty<bool> __depthWrite( + theContext, &NVRenderContext::IsDepthWriteEnabled, + &NVRenderContext::SetDepthWriteEnabled, false); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBoolOp::Enum> __depthFunction( + theContext, &NVRenderContext::GetDepthFunction, &NVRenderContext::SetDepthFunction, + qt3ds::render::NVRenderBoolOp::Less); + qt3ds::render::NVRenderContextScopedProperty<bool> __blendEnabled( + theContext, &NVRenderContext::IsBlendingEnabled, &NVRenderContext::SetBlendingEnabled, + false); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendFunctionArgument> + __blendFunction(theContext, &NVRenderContext::GetBlendFunction, + &NVRenderContext::SetBlendFunction, + qt3ds::render::NVRenderBlendFunctionArgument()); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendEquationArgument> + __blendEquation(theContext, &NVRenderContext::GetBlendEquation, + &NVRenderContext::SetBlendEquation, + qt3ds::render::NVRenderBlendEquationArgument()); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderFrameBufferPtr> + __rendertarget(theContext, &NVRenderContext::GetRenderTarget, + &NVRenderContext::SetRenderTarget); + + QT3DSU32 theSampleCount = 1; + bool isMultisamplePass = false; + if (theDesiredEnvironment.m_MSAAMode != AAModeValues::NoAA) { + switch (theDesiredEnvironment.m_MSAAMode) { + case AAModeValues::SSAA: + theSampleCount = 1; + isMultisamplePass = true; + break; + case AAModeValues::X2: + theSampleCount = 2; + isMultisamplePass = true; + break; + case AAModeValues::X4: + theSampleCount = 4; + isMultisamplePass = true; + break; + case AAModeValues::X8: + theSampleCount = 8; + isMultisamplePass = true; + break; + default: + QT3DS_ASSERT(false); + break; + }; + + // adjust render size for SSAA + if (theDesiredEnvironment.m_MSAAMode == AAModeValues::SSAA) { + CRendererUtil::GetSSAARenderSize( + theOriginalDesiredEnvironment.m_Width, theOriginalDesiredEnvironment.m_Height, + theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height); + } + } + CResourceFrameBuffer theFrameBuffer(*m_ResourceManager); + theFrameBuffer.EnsureFrameBuffer(); + NVRenderTexture2D *renderTargetTexture(theData.m_Texture); + qt3ds::render::NVRenderTextureTargetType::Enum fboAttachmentType = + qt3ds::render::NVRenderTextureTargetType::Texture2D; + if (isMultisamplePass) { + renderTargetTexture = NULL; + if (theSampleCount > 1) + fboAttachmentType = qt3ds::render::NVRenderTextureTargetType::Texture2D_MS; + } + + CResourceTexture2D renderColorTexture(*m_ResourceManager, renderTargetTexture); + + CResourceTexture2D renderDepthStencilTexture(*m_ResourceManager); + + if (theSampleCount > 1) + m_Context.GetRenderContext().SetMultisampleEnabled(true); + + qt3ds::render::NVRenderClearFlags theClearFlags; + NVRenderTextureFormats::Enum theDepthStencilTextureFormat(NVRenderTextureFormats::Unknown); + NVRenderFrameBufferAttachments::Enum theAttachmentLocation( + NVRenderFrameBufferAttachments::Unknown); + if (theDesiredEnvironment.m_Stencil) { + theDepthStencilTextureFormat = NVRenderTextureFormats::Depth24Stencil8; + theAttachmentLocation = NVRenderFrameBufferAttachments::DepthStencil; + } else if (theDesiredEnvironment.m_Depth != OffscreenRendererDepthValues::NoDepthBuffer) { + theAttachmentLocation = NVRenderFrameBufferAttachments::Depth; + switch (theDesiredEnvironment.m_Depth) { + case OffscreenRendererDepthValues::Depth16: + theDepthStencilTextureFormat = NVRenderTextureFormats::Depth16; + break; + case OffscreenRendererDepthValues::Depth24: + theDepthStencilTextureFormat = NVRenderTextureFormats::Depth24; + break; + case OffscreenRendererDepthValues::Depth32: + theDepthStencilTextureFormat = NVRenderTextureFormats::Depth32; + break; + default: + theAttachmentLocation = NVRenderFrameBufferAttachments::Unknown; + theDepthStencilTextureFormat = NVRenderTextureFormats::Unknown; + break; + } + } + renderColorTexture.EnsureTexture(theDesiredEnvironment.m_Width, + theDesiredEnvironment.m_Height, + theDesiredEnvironment.m_Format, theSampleCount); + theFrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0, *renderColorTexture, + fboAttachmentType); + + if (theDepthStencilTextureFormat != NVRenderTextureFormats::Unknown) { + renderDepthStencilTexture.EnsureTexture(theDesiredEnvironment.m_Width, + theDesiredEnvironment.m_Height, + theDepthStencilTextureFormat, theSampleCount); + theFrameBuffer->Attach(theAttachmentLocation, *renderDepthStencilTexture, + fboAttachmentType); + } + // IsComplete check takes a really long time so I am not going to worry about it for now. + + theContext.SetRenderTarget(theFrameBuffer); + theContext.SetViewport( + NVRenderRect(0, 0, theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height)); + theContext.SetScissorTestEnabled(false); + + theContext.SetBlendingEnabled(false); + theData.m_Renderer->Render(theDesiredEnvironment, theContext, thePresScaleFactor, + SScene::AlwaysClear, this); + + if (theSampleCount > 1) { + CResourceTexture2D theResult(*m_ResourceManager, theData.m_Texture); + + if (theDesiredEnvironment.m_MSAAMode != AAModeValues::SSAA) { + // Have to downsample the FBO. + CRendererUtil::ResolveMutisampleFBOColorOnly( + *m_ResourceManager, theResult, m_Context.GetRenderContext(), + theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height, + theDesiredEnvironment.m_Format, *theFrameBuffer); + + m_Context.GetRenderContext().SetMultisampleEnabled(false); + } else { + // Resolve the FBO to the layer texture + CRendererUtil::ResolveSSAAFBOColorOnly( + *m_ResourceManager, theResult, theOriginalDesiredEnvironment.m_Width, + theOriginalDesiredEnvironment.m_Height, m_Context.GetRenderContext(), + theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height, + theDesiredEnvironment.m_Format, *theFrameBuffer); + } + + QT3DS_ASSERT(theData.m_Texture == theResult.GetTexture()); + theResult.ForgetTexture(); + } else { + renderColorTexture.ForgetTexture(); + } + theFrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(), fboAttachmentType); + if (theAttachmentLocation != NVRenderFrameBufferAttachments::Unknown) + theFrameBuffer->Attach(theAttachmentLocation, NVRenderTextureOrRenderBuffer(), + fboAttachmentType); + } + + SOffscreenRenderResult GetRenderedItem(const SOffscreenRendererKey &inKey) override + { + TRendererMap::iterator theRenderer = m_Renderers.find(inKey); + QT3DSVec2 thePresScaleFactor = m_Context.GetPresentationScaleFactor(); + if (theRenderer != m_Renderers.end() && theRenderer->second.mPtr->m_Rendering == false) { + SRendererData &theData = *theRenderer->second.mPtr; + SScopedRenderDataRenderMarker __renderMarker(theData); + + bool renderedThisFrame = theData.m_Texture && theData.m_FrameCount == m_FrameCount; + theData.m_FrameCount = m_FrameCount; + // Two different quick-out pathways. + if (renderedThisFrame) + return theData; + + SOffscreenRendererEnvironment theDesiredEnvironment = + theData.m_Renderer->GetDesiredEnvironment(thePresScaleFactor); + // Ensure we get a valid width and height + theDesiredEnvironment.m_Width = + ITextRenderer::NextMultipleOf4(theDesiredEnvironment.m_Width); + theDesiredEnvironment.m_Height = + ITextRenderer::NextMultipleOf4(theDesiredEnvironment.m_Height); + if (theDesiredEnvironment.m_Width == 0 || theDesiredEnvironment.m_Height == 0) { + return SOffscreenRenderResult(); + } + + NVRenderRect theViewport(0, 0, theDesiredEnvironment.m_Width, + theDesiredEnvironment.m_Height); + IRenderList &theRenderList(m_Context.GetRenderList()); + NVRenderContext &theContext(m_Context.GetRenderContext()); + // This happens here because if there are any fancy render steps + SRenderListScopedProperty<bool> _scissor(theRenderList, + &IRenderList::IsScissorTestEnabled, + &IRenderList::SetScissorTestEnabled, false); + SRenderListScopedProperty<NVRenderRect> _viewport( + theRenderList, &IRenderList::GetViewport, &IRenderList::SetViewport, theViewport); + // Some plugins don't use the render list so they need the actual gl context setup. + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + theViewport); + + QT3DSU32 taskId = m_Context.GetRenderList().AddRenderTask( + *QT3DS_NEW(m_Context.GetPerFrameAllocator(), + SOffscreenRunnable)(*this, theData, theDesiredEnvironment)); + + SOffscreenRenderFlags theFlags = + theData.m_Renderer->NeedsRender(theDesiredEnvironment, thePresScaleFactor, this); + theData.m_HasTransparency = theFlags.m_HasTransparency; + theData.m_HasChangedSinceLastFrame = theFlags.m_HasChangedSinceLastFrame; + if (theData.m_Texture) { + // Quick-out if the renderer doesn't need to render itself. + if (theData.m_HasChangedSinceLastFrame == false) { + m_Context.GetRenderList().DiscardRenderTask(taskId); + return theData; + } + } else + theData.m_HasChangedSinceLastFrame = true; + + // Release existing texture if it doesn't match latest environment request. + if (theData.m_Texture) { + STextureDetails theDetails = theData.m_Texture->GetTextureDetails(); + if (theDesiredEnvironment.m_Width != theDetails.m_Width + || theDesiredEnvironment.m_Height != theDetails.m_Height + || theDesiredEnvironment.m_Format != theDetails.m_Format) { + m_ResourceManager->Release(*theData.m_Texture); + theData.m_Texture = NULL; + } + } + + if (theData.m_Texture == NULL) + theData.m_Texture = m_ResourceManager->AllocateTexture2D( + theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height, + theDesiredEnvironment.m_Format); + + // Add the node to the graph and get on with it. + + return theData; + } + return SOffscreenRenderResult(); + } + + void BeginFrame() override { m_PerFrameAllocator.reset(); } + void EndFrame() override { ++m_FrameCount; } +}; + +void SOffscreenRunnable::Run() +{ + m_RenderManager.RenderItem(m_Data, m_DesiredEnvironment); +} +} + +IOffscreenRenderManager &IOffscreenRenderManager::CreateOffscreenRenderManager( + NVAllocatorCallback &inCallback, IStringTable &inStringTable, IResourceManager &inManager, + IQt3DSRenderContext &inContext) +{ + return *QT3DS_NEW(inCallback, SOffscreenRenderManager)(inCallback, inStringTable, inManager, + inContext); +} diff --git a/src/runtimerender/Qt3DSOffscreenRenderManager.h b/src/runtimerender/Qt3DSOffscreenRenderManager.h new file mode 100644 index 0000000..6d82162 --- /dev/null +++ b/src/runtimerender/Qt3DSOffscreenRenderManager.h @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_OFFSCREEN_RENDER_MANAGER_H +#define QT3DS_OFFSCREEN_RENDER_MANAGER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/Qt3DSOption.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderLayer.h" + +namespace qt3ds { +namespace render { + class IResourceManager; + struct Qt3DSRenderPickResult; + class IGraphObjectPickQuery; + struct OffscreenRendererDepthValues + { + enum Enum { + NoDepthBuffer = 0, + Depth16, // 16 bit depth buffer + Depth24, // 24 bit depth buffer + Depth32, // 32 bit depth buffer + Depth24Stencil8 // 24 bit depth buffer 8 bit stencil buffer + }; + }; + + struct SOffscreenRendererEnvironment + { + QT3DSU32 m_Width; + QT3DSU32 m_Height; + NVRenderTextureFormats::Enum m_Format; + OffscreenRendererDepthValues::Enum m_Depth; + bool m_Stencil; + AAModeValues::Enum m_MSAAMode; + + SOffscreenRendererEnvironment() + : m_Width(0) + , m_Height(0) + , m_Format(NVRenderTextureFormats::Unknown) + , m_Depth(OffscreenRendererDepthValues::NoDepthBuffer) + , m_Stencil(false) + , m_MSAAMode(AAModeValues::NoAA) + { + } + + SOffscreenRendererEnvironment(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inFormat) + : m_Width(inWidth) + , m_Height(inHeight) + , m_Format(inFormat) + , m_Depth(OffscreenRendererDepthValues::Depth16) + , m_Stencil(false) + , m_MSAAMode(AAModeValues::NoAA) + { + } + + SOffscreenRendererEnvironment(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inFormat, + OffscreenRendererDepthValues::Enum inDepth, bool inStencil, + AAModeValues::Enum inAAMode) + : m_Width(inWidth) + , m_Height(inHeight) + , m_Format(inFormat) + , m_Depth(inDepth) + , m_Stencil(inStencil) + , m_MSAAMode(inAAMode) + { + } + + SOffscreenRendererEnvironment(const SOffscreenRendererEnvironment &inOther) + : m_Width(inOther.m_Width) + , m_Height(inOther.m_Height) + , m_Format(inOther.m_Format) + , m_Depth(inOther.m_Depth) + , m_Stencil(inOther.m_Stencil) + , m_MSAAMode(inOther.m_MSAAMode) + { + } + }; + + struct SOffscreenRenderFlags + { + bool m_HasTransparency; + bool m_HasChangedSinceLastFrame; + SOffscreenRenderFlags() + : m_HasTransparency(false) + , m_HasChangedSinceLastFrame(false) + { + } + + SOffscreenRenderFlags(bool transparency, bool hasChanged) + : m_HasTransparency(transparency) + , m_HasChangedSinceLastFrame(hasChanged) + { + } + }; + + typedef void *SRenderInstanceId; + + class IOffscreenRenderer : public NVRefCounted + { + public: + class IOffscreenRendererCallback + { + public: + virtual void onOffscreenRendererInitialized(const QString &id) = 0; + virtual void onOffscreenRendererFrame(const QString &id) = 0; + protected: + virtual ~IOffscreenRendererCallback() {} + }; + + protected: + virtual ~IOffscreenRenderer() {} + public: + virtual void addCallback(IOffscreenRendererCallback *cb) = 0; + // Arbitrary const char* returned to indicate the type of this renderer + // Can be overloaded to form the basis of an RTTI type system. + // Not currently used by the rendering system. + virtual CRegisteredString GetOffscreenRendererType() = 0; + virtual SOffscreenRendererEnvironment + GetDesiredEnvironment(QT3DSVec2 inPresentationScaleFactor) = 0; + // Returns true of this object needs to be rendered, false if this object is not dirty + virtual SOffscreenRenderFlags + NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) = 0; + // Returns true if the rendered result image has transparency, or false + // if it should be treated as a completely opaque image. + // It is the IOffscreenRenderer's job to clear any buffers (color, depth, stencil) that it + // needs to. It should not assume that it's buffers are clear; + // Sometimes we scale the width and height of the main presentation in order to fit a + // window. + // If we do so, the scale factor tells the subpresentation renderer how much the system has + // scaled. + virtual void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) = 0; + virtual void RenderWithClear(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + QT3DSVec4 inclearColor, + const SRenderInstanceId instanceId) = 0; + + // Implementors should implement one of the two interfaces below. + + // If this renderer supports picking that can return graph objects + // then return an interface here. + virtual IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId instanceId) = 0; + + // If you *don't* support the GraphObjectPickIterator interface, then you should implement + // this interface + // The system will just ask you to pick. + // If you return true, then we will assume that you swallowed the pick and will continue no + // further. + // else we will assume you did not and will continue the picking algorithm. + virtual bool Pick(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inViewportDimensions, + const SRenderInstanceId instanceId) = 0; + }; + + struct SOffscreenRenderResult + { + NVScopedRefCounted<IOffscreenRenderer> m_Renderer; + NVRenderTexture2D *m_Texture; + bool m_HasTransparency; + bool m_HasChangedSinceLastFrame; + + SOffscreenRenderResult(IOffscreenRenderer &inRenderer, NVRenderTexture2D &inTexture, + bool inTrans, bool inDirty) + : m_Renderer(&inRenderer) + , m_Texture(&inTexture) + , m_HasTransparency(inTrans) + , m_HasChangedSinceLastFrame(inDirty) + { + } + SOffscreenRenderResult() + : m_Renderer(NULL) + , m_Texture(NULL) + , m_HasTransparency(false) + , m_HasChangedSinceLastFrame(false) + { + } + }; + + struct SOffscreenRendererKey; + + /** + * The offscreen render manager attempts to satisfy requests for a given image under a given + *key. + * Renderers are throttled such that they render at most once per frame and potentially less + *than + * that if they don't require a new render. + */ + class IOffscreenRenderManager : public NVRefCounted + { + protected: + virtual ~IOffscreenRenderManager() {} + public: + // returns true if the renderer has not been registered. + // No return value means there was an error registering this id. + virtual Option<bool> MaybeRegisterOffscreenRenderer(const SOffscreenRendererKey &inKey, + IOffscreenRenderer &inRenderer) = 0; + virtual void RegisterOffscreenRenderer(const SOffscreenRendererKey &inKey, + IOffscreenRenderer &inRenderer) = 0; + virtual bool HasOffscreenRenderer(const SOffscreenRendererKey &inKey) = 0; + virtual IOffscreenRenderer *GetOffscreenRenderer(const SOffscreenRendererKey &inKey) = 0; + virtual void ReleaseOffscreenRenderer(const SOffscreenRendererKey &inKey) = 0; + + // This doesn't trigger rendering right away. A node is added to the render graph that + // points to this item. + // Thus rendering is deffered until the graph is run but we promise to render to this + // resource. + virtual SOffscreenRenderResult GetRenderedItem(const SOffscreenRendererKey &inKey) = 0; + // Called by the UICRenderContext, clients don't need to call this. + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + + static IOffscreenRenderManager & + CreateOffscreenRenderManager(NVAllocatorCallback &inCallback, IStringTable &inStringTable, + IResourceManager &inManager, IQt3DSRenderContext &inContext); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSOldNBustedRenderPlugin.cpp b/src/runtimerender/Qt3DSOldNBustedRenderPlugin.cpp new file mode 100644 index 0000000..4028373 --- /dev/null +++ b/src/runtimerender/Qt3DSOldNBustedRenderPlugin.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSOldNBustedRenderPlugin.h" +#include "SystemPrefix.h" +#include "Qt3DSDLLManager.h" +#include "render/Qt3DSRenderContext.h" + +#ifdef WIN32 +#pragma warning(disable : 4355) // this used in initializer list. I have never seen this result in + // a physical error +#endif + +namespace qt3ds { +namespace render { + + COldNBustedPluginRenderer::COldNBustedPluginRenderer(IQt3DSRenderContext &inRenderContext, + long inDLLHandle) + : m_RenderContext(inRenderContext) + , m_DLLHandle(inDLLHandle) + , mRefCount(0) + , m_OffscreenRendererType(inRenderContext.GetStringTable().RegisterStr(GetRendererName())) + { + if (m_DLLHandle != -1) { + Q3DStudio::CDLLManager &theManager = Q3DStudio::CDLLManager::GetDLLManager(); + + // Grab function procs + m_GetTextureSizeProc = reinterpret_cast<PROC_GetDesiredTextureSize>( + theManager.GetProc("GetDesiredTextureSize", m_DLLHandle)); + Q3DStudio_ASSERT(m_GetTextureSizeProc); + + m_RenderProc = reinterpret_cast<PROC_Render>(theManager.GetProc("Render", m_DLLHandle)); + Q3DStudio_ASSERT(m_RenderProc); + } + } + + NVRenderTextureFormats::Enum convertTextureFormat(ETEXTUREFORMAT fmt) + { + NVRenderTextureFormats::Enum ret = NVRenderTextureFormats::RGBA8; + switch (fmt) { + + case ETEXTUREFORMAT_RGBA8: + break; + + case ETEXTUREFORMAT_RGB8: + ret = NVRenderTextureFormats::RGB8; + break; + + default: + break; + + } + return ret; + } + + SOffscreenRendererEnvironment + COldNBustedPluginRenderer::GetDesiredEnvironment(QT3DSVec2 inPresScale) + { + long width, height; + ETEXTUREFORMAT format; + + m_GetTextureSizeProc(&width, &height, &format); + + return SOffscreenRendererEnvironment( + (QT3DSU32)(width * inPresScale.x), (QT3DSU32)(height * inPresScale.y), + convertTextureFormat(format), OffscreenRendererDepthValues::Depth24, false, + AAModeValues::NoAA); + } + + SOffscreenRenderFlags + COldNBustedPluginRenderer::NeedsRender(const SOffscreenRendererEnvironment & /*inEnvironment*/, + QT3DSVec2 /*inPresScale*/, + const SRenderInstanceId) + { + return SOffscreenRenderFlags(true, true); + } + + // Returns true if the rendered result image has transparency, or false + // if it should be treated as a completely opaque image. + void COldNBustedPluginRenderer::Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 /*inPresScale*/, + SScene::RenderClearCommand /*inClearColorBuffer*/, + const SRenderInstanceId) + { + inRenderContext.PushPropertySet(); + + m_RenderProc(inEnvironment.m_Width, inEnvironment.m_Height, 1); + + inRenderContext.PopPropertySet(true); + } +} +} diff --git a/src/runtimerender/Qt3DSOldNBustedRenderPlugin.h b/src/runtimerender/Qt3DSOldNBustedRenderPlugin.h new file mode 100644 index 0000000..2753bac --- /dev/null +++ b/src/runtimerender/Qt3DSOldNBustedRenderPlugin.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_OLD_N_BUSTED_RENDER_PLUGIN_H +#define QT3DS_OLD_N_BUSTED_RENDER_PLUGIN_H + +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderContextCore.h" +#include "foundation/Qt3DSVec4.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "Qt3DSPluginDLL.h" + +namespace qt3ds { +namespace render { + + class COldNBustedPluginRenderer; + + class COldNBustedPluginRenderer : public IOffscreenRenderer + { + public: + IQt3DSRenderContext &m_RenderContext; + long m_DLLHandle; + volatile QT3DSI32 mRefCount; + SOffscreenRendererEnvironment m_LastRenderedEnvironment; + CRegisteredString m_OffscreenRendererType; + + PROC_GetDesiredTextureSize m_GetTextureSizeProc; + PROC_Render m_RenderProc; + + COldNBustedPluginRenderer(IQt3DSRenderContext &inRenderContext, long inDLLHandle); + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) + + SOffscreenRendererEnvironment GetDesiredEnvironment(QT3DSVec2 inPresScale) override; + virtual SOffscreenRenderFlags + NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, QT3DSVec2 inPresScale, + const SRenderInstanceId instanceId) override; + void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext & /*inRenderContext*/, + QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, + const SRenderInstanceId instanceId) override; + void RenderWithClear(const SOffscreenRendererEnvironment &/*inEnvironment*/, + NVRenderContext &/*inRenderContext*/, + QT3DSVec2 /*inPresentationScaleFactor*/, + SScene::RenderClearCommand /*inColorBufferNeedsClear*/, + QT3DSVec4 /*inclearColor*/, + const SRenderInstanceId /*instanceId*/) override {} + IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId instanceId) override + { + Q_UNUSED(instanceId); + return NULL; + } + bool Pick(const QT3DSVec2 & /*inMouseCoords*/, const QT3DSVec2 & /*inViewportDimensions*/, + const SRenderInstanceId instanceId) override + { + Q_UNUSED(instanceId); + return false; + } + void addCallback(IOffscreenRendererCallback *cb) override + { + + } + // Used for RTTI purposes so we can safely static-cast an offscreen renderer to a + // CPluginRenderer + static const char *GetRendererName() { return "Plugin"; } + CRegisteredString GetOffscreenRendererType() override { return m_OffscreenRendererType; } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSOnscreenTextRenderer.cpp b/src/runtimerender/Qt3DSOnscreenTextRenderer.cpp new file mode 100644 index 0000000..4b74c51 --- /dev/null +++ b/src/runtimerender/Qt3DSOnscreenTextRenderer.cpp @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#define QT3DS_RENDER_ONSCREEN_TEXT +#ifdef QT3DS_RENDER_ONSCREEN_TEXT + +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderTextureAtlas.h" + +#include "EASTL/string.h" +#include "EASTL/hash_set.h" + +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/StrConvertUTF.h" + +#include "render/Qt3DSRenderContext.h" + +#include <QtGui/qpainter.h> +#include <QtGui/qimage.h> +#include <QtGui/qrawfont.h> +#include <QtCore/qfile.h> + +using namespace qt3ds::render; + +namespace { + +struct STextureAtlasFontEntry +{ + STextureAtlasFontEntry() + : m_x(0) + , m_y(0) + , m_width(0) + , m_height(0) + , m_xOffset(0) + , m_yOffset(0) + , m_advance(0) + , m_s(0) + , m_t(0) + , m_s1(0) + , m_t1(0) + { + } + + STextureAtlasFontEntry(float x, float y, float width, float height, float xoffset, + float yoffset, float advance, float s, float t, float s1, float t1) + : m_x(x) + , m_y(y) + , m_width(width) + , m_height(height) + , m_xOffset(xoffset) + , m_yOffset(yoffset) + , m_advance(advance) + , m_s(s) + , m_t(t) + , m_s1(s1) + , m_t1(t1) + { + } + + QT3DSF32 m_x; + QT3DSF32 m_y; + QT3DSF32 m_width; + QT3DSF32 m_height; + QT3DSF32 m_xOffset; + QT3DSF32 m_yOffset; + QT3DSF32 m_advance; + + QT3DSF32 m_s; + QT3DSF32 m_t; + QT3DSF32 m_s1; + QT3DSF32 m_t1; +}; + +typedef eastl::basic_string<char8_t, ForwardingAllocator> TStrType; +typedef nvhash_map<wchar_t, STextureAtlasFontEntry> TTextureAtlasMap; + +struct STextAtlasFont +{ + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + QT3DSU32 m_FontSize; + TTextureAtlasMap m_AtlasEntries; ///< our entries in the atlas + + STextAtlasFont(NVFoundationBase &inFoundation, QT3DSU32 fontSize) + : m_Foundation(inFoundation) + , mRefCount(0) + , m_FontSize(fontSize) + , m_AtlasEntries(inFoundation.getAllocator(), + "Qt3DSOnscreenRenderer::STextAtlasFont::m_AtlasEntrys") + { + } + + ~STextAtlasFont() { m_AtlasEntries.clear(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + static STextAtlasFont &CreateTextureAtlasFont(NVFoundationBase &inFnd, QT3DSU32 fontSize) + { + return *QT3DS_NEW(inFnd.getAllocator(), STextAtlasFont)(inFnd, fontSize); + } +}; + +// This class is only for rendering 2D screen aligned text +// it uses a predefined true type font and character set with various sizes +struct Qt3DSOnscreenTextRenderer : public ITextRenderer +{ + + static const QT3DSI32 TEXTURE_ATLAS_DIM = + 256; // if you change this you need to adjust STextTextureAtlas size as well + +private: + NVFoundationBase &m_Foundation; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + volatile QT3DSI32 mRefCount; + bool m_TextureAtlasInitialized; ///< true if atlas is setup + NVScopedRefCounted<ITextureAtlas> m_TextTextureAtlas; + NVScopedRefCounted<STextAtlasFont> m_TextFont; + QRawFont *m_font; +public: + Qt3DSOnscreenTextRenderer(NVFoundationBase &inFoundation) + : m_Foundation(inFoundation) + , mRefCount(0) + , m_TextureAtlasInitialized(false) + , m_font(nullptr) + { + } + + virtual ~Qt3DSOnscreenTextRenderer() + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + void AddSystemFontDirectory(const char8_t *) override {} + + virtual void AddProjectFontDirectory(CRegisteredString) + { + // We always render using the default font with on-screen renderer, + // so no need to care about font directories + } + + void AddProjectFontDirectory(const char8_t *inProjectDirectory) override + { + if (m_RenderContext) + AddProjectFontDirectory( + m_RenderContext->GetStringTable().RegisterStr(inProjectDirectory)); + } + + void loadFont() + { + // Ensure font. We can only render text of single size at the moment. + // Add a size map of fonts if it ever becomes necessary to render multiple font sizes. + if (!m_font) + m_font = new QRawFont(QStringLiteral(":res/Font/TitilliumWeb-Regular.ttf"), 20.0); + + // setup texture atlas + m_TextTextureAtlas = + ITextureAtlas::CreateTextureAtlas(m_RenderContext->GetFoundation(), *m_RenderContext, + TEXTURE_ATLAS_DIM, TEXTURE_ATLAS_DIM); + + // our list of predefined cached characters + QString cache = QStringLiteral(" !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"); + + m_TextFont = STextAtlasFont::CreateTextureAtlasFont(m_Foundation, 20); + CreateTextureAtlasEntries(m_font, *m_TextFont, cache); + m_TextureAtlasInitialized = true; + } + + void CreateTextureAtlasEntries(QRawFont *rawFont, STextAtlasFont &font, const QString &cache) + { + if (m_TextureAtlasInitialized || !m_TextTextureAtlas || !rawFont) + return; + + STextureAtlasRect theAtlasRect; + + QVector<quint32> glyphIndices = rawFont->glyphIndexesForString(cache); + QVector<QPointF> glyphAdvances = rawFont->advancesForGlyphIndexes(glyphIndices); + + for (int i = 0; i < glyphIndices.size(); i++) { + quint32 index = glyphIndices[i]; + QImage glyphImage; + + // blank char is not contained in a true type font + if (cache.at(i) != QChar(' ')) + glyphImage = rawFont->alphaMapForGlyph(index, QRawFont::PixelAntialiasing); + + QRectF rect = rawFont->boundingRect(index); + NVConstDataRef<QT3DSU8> bufferData(static_cast<const QT3DSU8 *>(glyphImage.bits()), + glyphImage.byteCount()); + + theAtlasRect = m_TextTextureAtlas->AddAtlasEntry( + glyphImage.width(), glyphImage.height(), + glyphImage.bytesPerLine(), glyphImage.width(), bufferData); + + if (theAtlasRect.m_Width != 0) { + font.m_AtlasEntries.insert( + eastl::make_pair( + cache.at(i).unicode(), STextureAtlasFontEntry( + (QT3DSF32)theAtlasRect.m_X, (QT3DSF32)theAtlasRect.m_Y, + (QT3DSF32)theAtlasRect.m_Width, (QT3DSF32)theAtlasRect.m_Height, + (QT3DSF32)rect.x(), (QT3DSF32)(0.0 - rect.height() - rect.y()), + glyphAdvances[i].x(), + theAtlasRect.m_NormX, theAtlasRect.m_NormY, + theAtlasRect.m_NormX + theAtlasRect.m_NormWidth, + theAtlasRect.m_NormY + theAtlasRect.m_NormHeight))); + } + } + } + + void ClearProjectFontDirectories() override {} + + ITextRenderer &GetTextRenderer(NVRenderContext &inRenderContext) override + { + m_RenderContext = inRenderContext; + return *this; + } + + void PreloadFonts() override {} + void ReloadFonts() override {} + + // unused + NVConstDataRef<SRendererFontEntry> GetProjectFontList() override + { + Q_ASSERT(false); + return NVConstDataRef<SRendererFontEntry>(); + } + + // unused + Option<CRegisteredString> GetFontNameForFont(CRegisteredString) override + { + Q_ASSERT(false); + return Empty(); + } + + // unused + Option<CRegisteredString> GetFontNameForFont(const char8_t *) override + { + Q_ASSERT(false); + return Empty(); + } + + // unused + STextDimensions MeasureText(const STextRenderInfo &, QT3DSF32, const char8_t *) override + { + Q_ASSERT(false); + return STextDimensions(0, 0); + } + + // unused + STextTextureDetails RenderText(const STextRenderInfo &, NVRenderTexture2D &) override + { + QT3DS_ASSERT(false); + return STextTextureDetails(); + } + + // unused + STextTextureDetails RenderText(const STextRenderInfo &, NVRenderPathFontItem &, + NVRenderPathFontSpecification &) override + { + QT3DS_ASSERT(false); + return STextTextureDetails(); + } + + SRenderTextureAtlasDetails RenderText(const STextRenderInfo &inText) override + { + qt3ds::foundation::IStringTable &theStringTable(m_RenderContext->GetStringTable()); + + const wchar_t *wText = theStringTable.GetWideStr(inText.m_Text); + QT3DSU32 length = (QT3DSU32)wcslen(wText); + + if (length) { + STextAtlasFont *pFont = m_TextFont; + const STextureAtlasFontEntry *pEntry; + QT3DSF32 x1, y1, x2, y2; + QT3DSF32 s, t, s1, t1; + QT3DSF32 advance = 0.0; + // allocate buffer for all the vertex data we need + // we construct triangles here + // which means character count x 6 vertices x 5 floats + QT3DSF32 *vertexData = + (QT3DSF32 *)QT3DS_ALLOC(m_Foundation.getAllocator(), + length * 6 * 5 * sizeof(QT3DSF32), + "Qt3DSOnscreenTextRenderer"); + QT3DSF32 *bufPtr = vertexData; + if (vertexData) { + for (size_t i = 0; i < length; ++i) { + pEntry = &pFont->m_AtlasEntries.find(wText[i])->second; + + if (pEntry) { + x1 = advance + pEntry->m_xOffset; + x2 = x1 + pEntry->m_width * inText.m_ScaleX; + y1 = pEntry->m_yOffset; + y2 = y1 + pEntry->m_height * inText.m_ScaleY; + + s = pEntry->m_s; + s1 = pEntry->m_s1; + t = pEntry->m_t; + t1 = pEntry->m_t1; + // store vertex data + bufPtr[0] = x1; + bufPtr[1] = y1; + bufPtr[2] = 0.0; + bufPtr[3] = s; + bufPtr[4] = t; + bufPtr[5] = x2; + bufPtr[6] = y1; + bufPtr[7] = 0.0; + bufPtr[8] = s1; + bufPtr[9] = t; + bufPtr[10] = x2; + bufPtr[11] = y2; + bufPtr[12] = 0.0; + bufPtr[13] = s1; + bufPtr[14] = t1; + + bufPtr[15] = x1; + bufPtr[16] = y1; + bufPtr[17] = 0.0; + bufPtr[18] = s; + bufPtr[19] = t; + bufPtr[20] = x2; + bufPtr[21] = y2; + bufPtr[22] = 0.0; + bufPtr[23] = s1; + bufPtr[24] = t1; + bufPtr[25] = x1; + bufPtr[26] = y2; + bufPtr[27] = 0.0; + bufPtr[28] = s; + bufPtr[29] = t1; + + advance += pEntry->m_advance * inText.m_ScaleX; + + bufPtr += 30; + } + } + + m_TextTextureAtlas->RelaseEntries(); + + return SRenderTextureAtlasDetails(length * 6, + toU8DataRef(vertexData, length * 6 * 5)); + } + } + + return SRenderTextureAtlasDetails(); + } + + STextTextureAtlasEntryDetails RenderAtlasEntry(QT3DSU32 index, + NVRenderTexture2D &inTexture) override + { + if (m_TextTextureAtlas) { + TTextureAtlasEntryAndBuffer theEntry = m_TextTextureAtlas->GetAtlasEntryByIndex(index); + if (theEntry.first.m_Width) { + inTexture.SetTextureData(theEntry.second, 0, theEntry.first.m_Width, + theEntry.first.m_Height, NVRenderTextureFormats::Alpha8); + inTexture.SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + inTexture.SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + inTexture.SetTextureWrapS(qt3ds::render::NVRenderTextureCoordOp::ClampToEdge); + inTexture.SetTextureWrapT(qt3ds::render::NVRenderTextureCoordOp::ClampToEdge); + STextureDetails theTextureDetails = inTexture.GetTextureDetails(); + return STextTextureAtlasEntryDetails(theTextureDetails.m_Width, + theTextureDetails.m_Height, theEntry.first.m_X, + theEntry.first.m_Y); + } + } + + return STextTextureAtlasEntryDetails(); + } + QT3DSI32 CreateTextureAtlas() override + { + loadFont(); + + QT3DSI32 count = 0; + if (m_TextTextureAtlas) + count = m_TextTextureAtlas->GetAtlasEntryCount(); + + return count; + } + + void BeginFrame() override {} + void EndFrame() override {} + void BeginPreloadFonts(IThreadPool &, IPerfTimer &) override {} + void EndPreloadFonts() override {} +}; +} + +ITextRendererCore &ITextRendererCore::CreateOnscreenTextRenderer(NVFoundationBase &inFnd) +{ + return *QT3DS_NEW(inFnd.getAllocator(), Qt3DSOnscreenTextRenderer)(inFnd); +} + +#endif // QT3DS_RENDER_ONSCREEN_TEXT diff --git a/src/runtimerender/Qt3DSQtTextRenderer.cpp b/src/runtimerender/Qt3DSQtTextRenderer.cpp new file mode 100644 index 0000000..029ccea --- /dev/null +++ b/src/runtimerender/Qt3DSQtTextRenderer.cpp @@ -0,0 +1,655 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSTextRenderer.h" +#include "Qt3DSRenderText.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" + +#include "foundation/Qt3DSVec2.h" +#include "foundation/FileTools.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderThreadPool.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "EASTL/set.h" +#include "EASTL/list.h" + +#include <QPainter> +#include <QImage> +#include <QFontDatabase> +#include <QDir> +#include <QDebug> +#include <QHash> +#include <QGuiApplication> +#include <QtMath> +#include <QRawFont> + +using namespace qt3ds::render; + +namespace { + +struct Qt3DSQtTextRenderer : public ITextRenderer +{ + struct FontInfo + { + QString fontFileName; + QString fontName; + QString fontFamily; + int fontId; + QFont font; + + FontInfo() : + fontId(-1) + {} + + FontInfo(const QString &fileName, const QString &name, const QString &family, int id) : + fontFileName(fileName) + , fontName(name) + , fontFamily(family) + , fontId(id) + { + font.setFamily(fontFamily); + } + + FontInfo(const FontInfo &other) : + fontFileName(other.fontFileName) + , fontName(other.fontName) + , fontFamily(other.fontFamily) + , fontId(other.fontId) + , font(other.font) + {} + + FontInfo &operator=(const FontInfo &other) + { + fontFileName = other.fontFileName; + fontName = other.fontName; + fontFamily = other.fontFamily; + fontId = other.fontId; + font = other.font; + + return *this; + } + }; + + typedef eastl::string TStrType; + typedef eastl::set<TStrType> TStringSet; + typedef QHash<QString, FontInfo> TFontInfoHash; + + NVFoundationBase &m_foundation; + NVScopedRefCounted<IStringTable> m_stringTable; + NVScopedRefCounted<NVRenderContext> m_renderContext; + NVScopedRefCounted<IPerfTimer> m_perfTimer; + volatile QT3DSI32 mRefCount; + nvvector<SRendererFontEntry> m_installedFonts; + + Sync m_PreloadSync; + + TStringSet m_systemFontDirs; + TStringSet m_projectFontDirs; + TFontInfoHash m_projectFontInfos; + TFontInfoHash m_systemFontInfos; + TStrType m_workspace; + + bool m_systemFontsInitialized; + bool m_projectFontsInitialized; + bool m_PreloadingFonts; + + QStringList m_nameFilters; + qreal m_pixelRatio; + + Qt3DSQtTextRenderer(NVFoundationBase &inFoundation, IStringTable &inStrTable) + : m_foundation(inFoundation) + , m_stringTable(inStrTable) + , mRefCount(0) + , m_installedFonts(inFoundation.getAllocator(), "Qt3DSQtTextRenderer::m_installedFonts") + , m_PreloadSync(inFoundation.getAllocator()) + , m_systemFontsInitialized(false) + , m_projectFontsInitialized(false) + , m_PreloadingFonts(false) + , m_pixelRatio(1.0) + { + const QWindowList list = QGuiApplication::topLevelWindows(); + if (list.size() > 0) + m_pixelRatio = list[0]->devicePixelRatio(); + + m_nameFilters << QStringLiteral("*.ttf"); + m_nameFilters << QStringLiteral("*.otf"); + } + virtual ~Qt3DSQtTextRenderer() + { + QFontDatabase::removeAllApplicationFonts(); + } + + QString stringToQString(const CRegisteredString &str) + { + return QString::fromUtf8(str.c_str()); + } + + QString stringToQString(const eastl::string &str) + { + return QString::fromUtf8(str.c_str()); + } + + QString stringToQString(const char8_t *str) + { + return QString::fromUtf8(str); + } + + CRegisteredString QStringToRegisteredString(const QString &str) + { + return m_stringTable->RegisterStr(str.toUtf8().constData()); + } + + void unregisterProjectFonts() + { + for (FontInfo &fi : m_projectFontInfos.values()) + QFontDatabase::removeApplicationFont(fi.fontId); + m_projectFontsInitialized = false; + m_installedFonts.clear(); + m_projectFontInfos.clear(); + } + + QString getFileStem(const QString &fileName) + { + QString retVal; + int dotPos = fileName.lastIndexOf(QChar('.')); + if (dotPos < 0) + return retVal; + int slashPos = fileName.lastIndexOf(QChar('/')); + retVal = fileName.mid(slashPos + 1); + retVal.chop(fileName.length() - dotPos); + return retVal; + } + + void registerFonts(TStringSet dirSet, TFontInfoHash *fontInfos = nullptr) + { + for (TStringSet::const_iterator theIter = dirSet.begin(), + theEnd = dirSet.end(); + theIter != theEnd; ++theIter) { + QString localDir = CFileTools::NormalizePathForQtUsage(stringToQString(*theIter)); + QDir dir(localDir); + if (!dir.exists()) { + qWarning("Attempted to register invalid font directory: %s", + qPrintable(localDir)); + continue; + } + QStringList entryList = dir.entryList(m_nameFilters); + for (QString entry : entryList) { + entry = dir.absoluteFilePath(entry); + QFile file(entry); + if (file.open(QIODevice::ReadOnly)) { + QByteArray rawData = file.readAll(); + int fontId = QFontDatabase::addApplicationFontFromData(rawData); + if (fontId < 0) { + qCWarning(WARNING, "Failed to register font: %s", + entry.toStdString().c_str()); + } else if (fontInfos) { + QString fontName = getFileStem(entry); + QString fontFamily; + QStringList families = QFontDatabase::applicationFontFamilies(fontId); + if (families.size() > 0) + fontFamily = families.at(0); + FontInfo fi(entry, fontName, fontFamily, fontId); + // Detect font style and weight using a dummy QRawFont + QRawFont rawFont(rawData, 16); + if (rawFont.isValid()) { + if (rawFont.style() != QFont::StyleOblique) { + fi.font.setStyle(rawFont.style()); + fi.font.setWeight(rawFont.weight()); + } + } else { + qCWarning(WARNING, "Failed to determine font style: %s", + entry.toStdString().c_str()); + } + fontInfos->insert(fontName, fi); + } + } else { + qCWarning(WARNING, "Failed to load font: %s", + entry.toStdString().c_str()); + } + } + } + } + + void projectCleanup() + { + m_projectFontsInitialized = false; + unregisterProjectFonts(); + m_projectFontDirs.clear(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_foundation.getAllocator()) + + eastl::pair<TStrType, bool> AddFontDirectory(const TStrType &inDirectory, TStringSet &inDirSet) + { + if (inDirectory.empty()) { + m_workspace.assign("./"); + } else { + m_workspace.clear(); + for (const char8_t *item = inDirectory.c_str(); item && *item; ++item) { + if (*item == '\\') + m_workspace.append(1, '/'); + else + m_workspace.append(1, static_cast<char8_t>(*item)); + } + if (m_workspace.back() != '/') + m_workspace.append(1, '/'); + } + + return eastl::make_pair(m_workspace, inDirSet.insert(m_workspace).second); + } + + // You can have several standard font directories and these will be persistent + void AddSystemFontDirectory(const char8_t *inDirectory) override + { + AddFontDirectory(inDirectory, m_systemFontDirs); + } + + void AddProjectFontDirectory(const char8_t *inProjectDirectory) override + { + eastl::pair<TStrType, bool> theAddResult = + AddFontDirectory(inProjectDirectory, m_projectFontDirs); + if (theAddResult.second && m_projectFontsInitialized) + ReloadFonts(); + } + + void ReloadFonts() override + { + unregisterProjectFonts(); + PreloadFonts(); + } + + void PreloadFonts() override + { + if (!m_systemFontsInitialized) { + m_systemFontsInitialized = true; + registerFonts(m_systemFontDirs, &m_systemFontInfos); + } + + if (!m_projectFontsInitialized) { + m_projectFontsInitialized = true; + registerFonts(m_projectFontDirs, &m_projectFontInfos); + } + } + + void ClearProjectFontDirectories() override + { + projectCleanup(); + } + + static void PreloadThreadCallback(void *inData) + { + Qt3DSQtTextRenderer *theRenderer(reinterpret_cast<Qt3DSQtTextRenderer *>(inData)); + theRenderer->PreloadFonts(); + theRenderer->m_PreloadSync.set(); + } + + void BeginPreloadFonts(IThreadPool &inThreadPool, IPerfTimer &inTimer) override + { + m_PreloadingFonts = true; + + m_PreloadSync.reset(); + m_perfTimer = inTimer; + + inThreadPool.AddTask(this, PreloadThreadCallback, NULL); + } + + void EndPreloadFonts() override + { + if (m_PreloadingFonts) { + { + SStackPerfTimer __perfTimer(*m_perfTimer, "QtText: Wait till font preloading completed"); + m_PreloadSync.wait(); + } + } + m_PreloadingFonts = false; + } + + // Get the list of project fonts. These are the only fonts that can be displayed. + NVConstDataRef<SRendererFontEntry> GetProjectFontList() override + { + PreloadFonts(); + if (m_installedFonts.empty()) { + m_installedFonts.reserve(m_projectFontInfos.size()); + for (FontInfo &fi : m_projectFontInfos.values()) { + m_installedFonts.push_back(SRendererFontEntry( + fi.fontName, + fi.fontFileName)); + } + + } + return m_installedFonts; + } + + Option<CRegisteredString> GetFontNameForFont(CRegisteredString inFontname) override + { + // This function is there to support legacy font names. + + QString inStr = stringToQString(inFontname); + if (m_projectFontInfos.keys().contains(inStr)) + return inFontname; + + // Fall back for family name detection if not found by font name + for (FontInfo &fi : m_projectFontInfos.values()) { + if (inStr == fi.fontFamily) + return QStringToRegisteredString(fi.fontName); + } + + return Empty(); + } + + Option<CRegisteredString> GetFontNameForFont(const char8_t *inFontname) override + { + return GetFontNameForFont(m_stringTable->RegisterStr(inFontname)); + } + + ITextRenderer &GetTextRenderer(NVRenderContext &inRenderContext) override + { + m_renderContext = inRenderContext; + return *this; + } + + FontInfo &fontInfoForName(const CRegisteredString &fontName) + { + PreloadFonts(); + QString qtFontName = stringToQString(fontName); + if (m_projectFontInfos.contains(qtFontName)) + return m_projectFontInfos[qtFontName]; + + if (m_systemFontInfos.contains(qtFontName)) + return m_systemFontInfos[qtFontName]; + + // Unknown font, create a system font for it + FontInfo fi("", qtFontName, qtFontName, -1); + m_systemFontInfos.insert(qtFontName, fi); + + return m_systemFontInfos[qtFontName]; + } + + void updateFontInfo(FontInfo &fi, const STextRenderInfo &inText, + QT3DSF32 inTextScaleFactor = 1.0f) + { + qreal pixelSize = inText.m_FontSize; + fi.font.setPixelSize(pixelSize * inTextScaleFactor); + fi.font.setLetterSpacing(QFont::AbsoluteSpacing, qreal(inText.m_Tracking)); + } + + QStringList splitText(const char8_t *theText) + { + // Split the text into lines + int lines = 1; + int lineLen = 0; + QStringList lineList; + const char8_t *lineStartItem = nullptr; + for (const char8_t *item = theText; item && *item; ++item) { + if (!lineLen) + lineStartItem = item; + ++lineLen; + if (*item == '\n') { + int chopAmount = 1; + if (lineLen > 1 && *(item - 1) == '\r') + ++chopAmount; + + ++lines; + lineList.append(QString::fromUtf8(lineStartItem, lineLen - chopAmount)); + lineLen = 0; + } + } + if (lineStartItem) + lineList.append(QString::fromUtf8(lineStartItem, lineLen)); + + return lineList; + } + + QRectF textBoundingBox(const STextRenderInfo &inText, + const QFontMetricsF &fm, QStringList &lineList, + QVector<qreal> &lineWidths, const char8_t *inTextOverride = nullptr) + { + const char8_t *theText = inTextOverride ? inTextOverride : inText.m_Text.c_str(); + lineList = splitText(theText); + + QRectF boundingBox; + boundingBox.setHeight(lineList.size() * fm.height() + qCeil(qreal(lineList.size() - 1) * qreal(inText.m_Leading))); + + lineWidths.resize(lineList.size()); + + for (int i = 0; i < lineList.size(); ++i) { + // For italicized fonts the bounding box right is the correct method + // to measure since the left offset may extend, but for + // non-italicized fonts we need the width method to meausure + // otherwise the resultant text will be clipped. + QString line = lineList.at(i); + qreal width = fm.width(line); + qreal right = fm.boundingRect(line).right(); + // For hdpi displays, fontmetrics doesn't always calculate enough space for fonts, so + // we add the pixel ratio to all widths to avoid clipping + qreal lineWidth = qMax(width, right) + m_pixelRatio; + lineWidths[i] = lineWidth; + if (boundingBox.width() < lineWidth) + boundingBox.setWidth(lineWidth); + } + + // We don't want extra letter spacing on the last glyph, so let's remove it + boundingBox.setRight(qMax(boundingBox.left(), boundingBox.right() - qFloor(inText.m_Tracking))); + + return boundingBox; + } + + STextDimensions MeasureText(const STextRenderInfo &inText, QT3DSF32 inTextScaleFactor, + const char8_t *inTextOverride) override + { + FontInfo &fi = fontInfoForName(inText.m_Font); + updateFontInfo(fi, inText, inTextScaleFactor); + QFontMetricsF fm(fi.font); + QStringList dummyList; + QVector<qreal> dummyWidth; + QRectF boundingBox = textBoundingBox(inText, fm, dummyList, dummyWidth, inTextOverride); + return STextDimensions(boundingBox.width(), boundingBox.height()); + } + + int alignToQtAlign(TextVerticalAlignment::Enum va) + { + int qtAlign(0); + switch (va) { + case TextVerticalAlignment::Top: + qtAlign = Qt::AlignTop; + break; + case TextVerticalAlignment::Bottom: + qtAlign = Qt::AlignBottom; + break; + default: + qtAlign = Qt::AlignVCenter; + } + + return qtAlign; + } + + STextTextureDetails RenderText(const STextRenderInfo &inSrcText, + NVRenderTexture2D &inTexture) override + { + FontInfo &fi = fontInfoForName(inSrcText.m_Font); + updateFontInfo(fi, inSrcText); + QFontMetricsF fm(fi.font); + int horizontalAlignmentFlag = Qt::AlignLeft; + + int shadowRgb = int(2.55f * (100 - int(inSrcText.m_DropShadowStrength))); + QStringList lineList; + QVector<qreal> lineWidths; + QRectF boundingBox; + const bool dynamicTextArea = inSrcText.m_BoundingBox.isZero(); + + if (dynamicTextArea) { + boundingBox = textBoundingBox(inSrcText, fm, lineList, lineWidths); + } else { + lineList << inSrcText.m_Text.c_str(); + lineWidths << inSrcText.m_BoundingBox.x; + boundingBox = QRectF(0, 0, inSrcText.m_BoundingBox.x, inSrcText.m_BoundingBox.y); + } + + if (boundingBox.width() <= 0 || boundingBox.height() <= 0) { + return ITextRenderer::UploadData(toU8DataRef((char *)nullptr, 0), inTexture, 4, 4, + 0, 0, + NVRenderTextureFormats::RGBA8, true); + } + + int finalWidth = NextMultipleOf4(boundingBox.width()); + int finalHeight = NextMultipleOf4(boundingBox.height()); + + QImage image(finalWidth, finalHeight, QImage::Format_ARGB32); + image.fill(0); + QPainter painter(&image); + painter.setPen(Qt::white); + painter.setFont(fi.font); + + // Translate painter to remove the extra spacing of the last letter + qreal tracking = 0.0; + switch (inSrcText.m_HorizontalAlignment) { + case TextHorizontalAlignment::Center: + horizontalAlignmentFlag = Qt::AlignHCenter; + tracking += qreal(inSrcText.m_Tracking / 2.0f); + break; + case TextHorizontalAlignment::Right: + horizontalAlignmentFlag = Qt::AlignRight; + tracking += qreal(inSrcText.m_Tracking); + break; + default: + break; // Do nothing + } + + int wordWrapFlags = 0; + if (dynamicTextArea) { + wordWrapFlags = Qt::TextDontClip; + } else { + switch (inSrcText.m_WordWrap) { + case TextWordWrap::WrapWord: + wordWrapFlags = Qt::TextWordWrap | Qt::TextDontClip; + break; + case TextWordWrap::WrapAnywhere: + wordWrapFlags = Qt::TextWrapAnywhere | Qt::TextDontClip; + break; + case TextWordWrap::Clip: + default: + break; + } + } + + int lineHeight = dynamicTextArea ? fm.height() : finalHeight; + QT3DSF32 nextHeight = 0; + for (int i = 0; i < lineList.size(); ++i) { + const QString &line = lineList.at(i); + qreal xTranslation = tracking; + switch (inSrcText.m_HorizontalAlignment) { + case TextHorizontalAlignment::Center: + xTranslation += qreal(boundingBox.width() - lineWidths.at(i)) / 2.0; + break; + case TextHorizontalAlignment::Right: + xTranslation += qreal(boundingBox.width() - lineWidths.at(i)); + break; + default: + break; // Do nothing + } + QRectF bound(xTranslation, qreal(nextHeight), lineWidths.at(i), lineHeight); + QRectF actualBound; + if (inSrcText.m_DropShadow) { + qreal shadowOffsetX = qreal(inSrcText.m_FontSize * inSrcText.m_DropShadowOffsetX) + / 1000.; + qreal shadowOffsetY = qreal(inSrcText.m_FontSize * inSrcText.m_DropShadowOffsetY) + / 1000.; + QRectF boundShadow(xTranslation + shadowOffsetX, nextHeight + shadowOffsetY, + qreal(lineWidths.at(i)), lineHeight); + // shadow is a darker shade of the given font color + painter.setPen(QColor(shadowRgb, shadowRgb, shadowRgb)); + painter.drawText(boundShadow, + alignToQtAlign(inSrcText.m_VerticalAlignment) | wordWrapFlags + | horizontalAlignmentFlag, line, &actualBound); + painter.setPen(Qt::white); // coloring is done in the shader + } + painter.drawText(bound, + alignToQtAlign(inSrcText.m_VerticalAlignment) | wordWrapFlags + | horizontalAlignmentFlag, line, &actualBound); + + nextHeight += QT3DSF32(lineHeight) + inSrcText.m_Leading; + } + + return ITextRenderer::UploadData(toU8DataRef(image.bits(), image.byteCount()), inTexture, + image.width(), image.height(), + image.width(), image.height(), + NVRenderTextureFormats::RGBA8, true); + } + + STextTextureDetails RenderText(const STextRenderInfo &inText, + NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inFontPathSpec) override + { + Q_UNUSED(inText); + Q_UNUSED(inPathFontItem); + Q_UNUSED(inFontPathSpec); + QT3DS_ASSERT(m_renderContext->IsPathRenderingSupported()); + + // We do not support HW accelerated fonts (yet?) + QT3DS_ASSERT(false); + + return STextTextureDetails(); + } + + void BeginFrame() override + { + // Nothing to do + } + + void EndFrame() override + { + // Nothing to do + } + + // unused for text rendering via texture atlas + STextTextureAtlasEntryDetails RenderAtlasEntry(QT3DSU32, NVRenderTexture2D &) override + { + return STextTextureAtlasEntryDetails(); + } + QT3DSI32 CreateTextureAtlas() override + { + return 0; + } + SRenderTextureAtlasDetails RenderText(const STextRenderInfo &) override + { + return SRenderTextureAtlasDetails(); + } +}; +} + +ITextRendererCore &ITextRendererCore::CreateQtTextRenderer(NVFoundationBase &inFnd, + IStringTable &inStrTable) +{ + return *QT3DS_NEW(inFnd.getAllocator(), Qt3DSQtTextRenderer)(inFnd, inStrTable); +} diff --git a/src/runtimerender/Qt3DSRender.h b/src/runtimerender/Qt3DSRender.h new file mode 100644 index 0000000..e07dc6e --- /dev/null +++ b/src/runtimerender/Qt3DSRender.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_H +#define QT3DS_RENDER_H + +namespace qt3ds { +class NVAllocatorCallback; +class NVFoundationBase; +namespace foundation { + class CRegisteredString; + class IStringTable; + class CStrTableOrDataRef; + struct SStrRemapMap; + struct SWriteBuffer; + struct SDataReader; + struct SPtrOffsetMap; + class IPerfTimer; + class Qt3DSString; + class Qt3DSStringUtils; +} +namespace intrinsics { +} +namespace render { + class NVRenderTexture2D; + class NVRenderTexture2DArray; + class NVRenderTextureCube; + class NVRenderImage2D; + class NVRenderFrameBuffer; + class NVRenderRenderBuffer; + class NVRenderVertexBuffer; + class NVRenderIndexBuffer; + class NVRenderDrawIndirectBuffer; + class NVRenderInputAssembler; + class NVRenderAttribLayout; + class NVRenderDepthStencilState; + class NVRenderContext; + class NVRenderConstantBuffer; + class NVRenderShaderProgram; + class NVRenderShaderConstantBase; + struct NVRenderDrawMode; + struct NVRenderWinding; + struct NVRenderSrcBlendFunc; + struct NVRenderBlendEquation; + struct NVRenderState; + struct NVRenderTextureCoordOp; + struct NVRenderTextureMagnifyingOp; + struct NVRenderDstBlendFunc; + struct NVRenderContextValues; + struct NVRenderClearValues; + struct NVRenderRect; + struct NVRenderRectF; + struct NVRenderRenderBufferFormats; + struct NVRenderTextureFormats; + struct NVRenderTextureSwizzleMode; + struct NVRenderFrameBufferAttachments; + struct NVRenderRect; + struct NVRenderTextureCubeFaces; + struct STextureDetails; + struct NVRenderShaderDataTypes; + class NVRenderTextureOrRenderBuffer; + struct NVRenderTextureMinifyingOp; + struct NVReadFaces; + struct NVRenderVertexBufferEntry; + struct NVRenderPtrPtrMap; + class NVRenderComputeShader; + class NVRenderAttribLayout; + struct NVRenderBufferAccessTypeValues; + struct NVRenderImageAccessType; + struct NVRenderBufferBindValues; + struct DrawArraysIndirectCommand; + struct NVRenderShaderTypeValue; + class NVRenderPathRender; + class NVRenderPathSpecification; + class NVRenderPathFontSpecification; + class NVRenderPathFontItem; + struct NVRenderTextureTypeValue; + class NVRenderProgramPipeline; +} +class NVPlane; +} + +namespace eastl { +} + +namespace qt3ds { + +namespace render { + using namespace qt3ds; + using namespace qt3ds::foundation; + using namespace qt3ds::intrinsics; + using qt3ds::render::NVRenderTexture2D; + using qt3ds::render::NVRenderTexture2DArray; + using qt3ds::render::NVRenderTextureCube; + using qt3ds::render::NVRenderImage2D; + using qt3ds::render::NVRenderFrameBuffer; + using qt3ds::render::NVRenderRenderBuffer; + using qt3ds::render::NVRenderVertexBuffer; + using qt3ds::render::NVRenderIndexBuffer; + using qt3ds::render::NVRenderDrawIndirectBuffer; + using qt3ds::render::NVRenderInputAssembler; + using qt3ds::render::NVRenderAttribLayout; + using qt3ds::render::NVRenderDepthStencilState; + using qt3ds::render::NVRenderContext; + using qt3ds::render::NVRenderConstantBuffer; + using qt3ds::render::NVRenderShaderProgram; + using qt3ds::render::NVRenderShaderConstantBase; + using qt3ds::render::NVRenderDrawMode; + using qt3ds::render::NVRenderWinding; + using qt3ds::foundation::CRegisteredString; + using qt3ds::foundation::IStringTable; + using qt3ds::render::NVRenderSrcBlendFunc; + using qt3ds::render::NVRenderBlendEquation; + using qt3ds::render::NVRenderState; + using qt3ds::foundation::IStringTable; + using qt3ds::foundation::CRegisteredString; + using qt3ds::render::NVRenderTextureCoordOp; + using qt3ds::render::NVRenderDstBlendFunc; + using qt3ds::render::NVRenderRect; + using qt3ds::render::NVRenderRectF; + using qt3ds::render::NVRenderRenderBufferFormats; + using qt3ds::render::NVRenderTextureFormats; + using qt3ds::render::NVRenderTextureSwizzleMode; + using qt3ds::render::NVRenderFrameBufferAttachments; + using qt3ds::render::NVRenderRect; + using qt3ds::render::NVRenderContextValues; + using qt3ds::render::NVRenderClearValues; + using qt3ds::render::STextureDetails; + using qt3ds::render::NVRenderShaderDataTypes; + using qt3ds::render::NVRenderTextureMagnifyingOp; + using qt3ds::render::NVRenderTextureOrRenderBuffer; + using qt3ds::render::NVRenderTextureMinifyingOp; + using qt3ds::render::NVReadFaces; + using qt3ds::render::NVRenderTextureCubeFaces; + using qt3ds::foundation::SStrRemapMap; + using qt3ds::foundation::SWriteBuffer; + using qt3ds::foundation::SDataReader; + using qt3ds::foundation::Qt3DSString; + using qt3ds::foundation::Qt3DSStringUtils; + using qt3ds::render::NVRenderPtrPtrMap; + using qt3ds::foundation::CStrTableOrDataRef; + using qt3ds::foundation::SPtrOffsetMap; + using qt3ds::foundation::IPerfTimer; + using qt3ds::render::NVRenderVertexBufferEntry; + using qt3ds::render::NVRenderComputeShader; + using qt3ds::render::NVRenderAttribLayout; + using qt3ds::render::NVRenderBufferAccessTypeValues; + using qt3ds::render::NVRenderImageAccessType; + using qt3ds::render::NVRenderBufferBindValues; + using qt3ds::render::DrawArraysIndirectCommand; + using qt3ds::render::NVRenderShaderTypeValue; + using qt3ds::render::NVRenderPathRender; + using qt3ds::render::NVRenderPathSpecification; + using qt3ds::render::NVRenderPathFontSpecification; + using qt3ds::render::NVRenderPathFontItem; + using qt3ds::render::NVRenderTextureTypeValue; + using qt3ds::render::NVRenderProgramPipeline; + + class IQt3DSRenderContextCore; + class IQt3DSRenderContext; + class IQt3DSRenderer; + class IBufferManager; + struct SRenderMesh; + class IRenderableObject; + class IQt3DSRenderer; + class IBufferManager; + class IResourceManager; + class IOffscreenRenderManager; + struct SNode; + struct SGraphObject; + class ITextRenderer; + class ITextRendererCore; + class IInputStreamFactory; + class IRefCountedInputStream; + class IEffectSystem; + class IEffectSystemCore; + class IShaderCache; + class IQt3DSRenderNodeFilter; + class IRenderWidget; + class IRenderWidgetContext; + struct SShaderVertexCodeGenerator; + struct SShaderFragmentCodeGenerator; + class IThreadPool; + struct SRenderMesh; + struct SLoadedTexture; + class IImageBatchLoader; + class ITextTextureCache; + class ITextTextureAtlas; + class IRenderPluginInstance; + class IRenderPluginClass; + class IRenderPluginManager; + class IRenderPluginManagerCore; + struct SRenderPlugin; + class IDynamicObjectSystemCore; + class IDynamicObjectSystem; + class IDynamicObjectClass; + struct SRenderSubset; + struct SModel; + namespace dynamic { + struct SPropertyDefinition; + } + struct SLight; + struct SCamera; + struct SCustomMaterial; + class ICustomMaterialSystem; + class ICustomMaterialSystemCore; + struct SLayer; + struct SReferencedMaterial; + struct SPGGraphObject; + class IPixelGraphicsRenderer; + class IBufferLoader; + struct SEffect; + class IRenderList; + class IRenderTask; + class CResourceTexture2D; + class IPathManagerCore; + class IPathManager; + struct SPath; + struct SPathSubPath; + class IShaderProgramGenerator; + class IShaderStageGenerator; + class IDefaultMaterialShaderGenerator; + class ICustomMaterialShaderGenerator; + struct SRenderableImage; + class Qt3DSShadowMap; + struct SLightmaps; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderClippingFrustum.cpp b/src/runtimerender/Qt3DSRenderClippingFrustum.cpp new file mode 100644 index 0000000..8637556 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderClippingFrustum.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderClippingFrustum.h" + +using namespace qt3ds::render; + +SClippingFrustum::SClippingFrustum(const QT3DSMat44 &modelviewprojection, SClipPlane nearPlane) +{ + SClipPlane *_cullingPlanes = mPlanes; + const QT3DSMat44 &modelViewProjectionMat(modelviewprojection); + const QT3DSF32 *modelviewProjection = modelViewProjectionMat.front(); + +// update planes (http://read.pudn.com/downloads128/doc/542641/Frustum.pdf) +// Google for Gribb plane extraction if that link doesn't work. +// http://www.google.com/search?q=ravensoft+plane+extraction +#define M(_x, _y) modelviewProjection[(4 * (_y)) + (_x)] + // left plane + _cullingPlanes[0].normal.x = M(3, 0) + M(0, 0); + _cullingPlanes[0].normal.y = M(3, 1) + M(0, 1); + _cullingPlanes[0].normal.z = M(3, 2) + M(0, 2); + _cullingPlanes[0].d = M(3, 3) + M(0, 3); + _cullingPlanes[0].d /= _cullingPlanes[0].normal.normalize(); + + // right plane + _cullingPlanes[1].normal.x = M(3, 0) - M(0, 0); + _cullingPlanes[1].normal.y = M(3, 1) - M(0, 1); + _cullingPlanes[1].normal.z = M(3, 2) - M(0, 2); + _cullingPlanes[1].d = M(3, 3) - M(0, 3); + _cullingPlanes[1].d /= _cullingPlanes[1].normal.normalize(); + + // far plane + _cullingPlanes[2].normal.x = M(3, 0) - M(2, 0); + _cullingPlanes[2].normal.y = M(3, 1) - M(2, 1); + _cullingPlanes[2].normal.z = M(3, 2) - M(2, 2); + _cullingPlanes[2].d = M(3, 3) - M(2, 3); + _cullingPlanes[2].d /= _cullingPlanes[2].normal.normalize(); + + // bottom plane + _cullingPlanes[3].normal.x = M(3, 0) + M(1, 0); + _cullingPlanes[3].normal.y = M(3, 1) + M(1, 1); + _cullingPlanes[3].normal.z = M(3, 2) + M(1, 2); + _cullingPlanes[3].d = M(3, 3) + M(1, 3); + _cullingPlanes[3].d /= _cullingPlanes[3].normal.normalize(); + + // top plane + _cullingPlanes[4].normal.x = M(3, 0) - M(1, 0); + _cullingPlanes[4].normal.y = M(3, 1) - M(1, 1); + _cullingPlanes[4].normal.z = M(3, 2) - M(1, 2); + _cullingPlanes[4].d = M(3, 3) - M(1, 3); + _cullingPlanes[4].d /= _cullingPlanes[4].normal.normalize(); +#undef M + _cullingPlanes[5] = nearPlane; + // http://www.openscenegraph.org/projects/osg/browser/OpenSceneGraph/trunk/include/osg/Plane?rev=5328 + // setup the edges of the plane that we will clip against an axis-aligned bounding box. + for (QT3DSU32 idx = 0; idx < 6; ++idx) { + _cullingPlanes[idx].calculateBBoxEdges(); + } +} diff --git a/src/runtimerender/Qt3DSRenderClippingFrustum.h b/src/runtimerender/Qt3DSRenderClippingFrustum.h new file mode 100644 index 0000000..b788ad2 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderClippingFrustum.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CLIPPING_PLANE_H +#define QT3DS_RENDER_CLIPPING_PLANE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSPlane.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/Qt3DSBounds3.h" + +namespace qt3ds { +namespace render { + + struct BoxEdgeFlagValues + { + enum Enum { + xMax = 1, + yMax = 1 << 1, + zMax = 1 << 2, + }; + }; + + typedef NVFlags<BoxEdgeFlagValues::Enum, QT3DSU8> TRenderBoxEdge; + + // For an intesection test, we only need two points of the bounding box. + // There will be a point nearest to the plane, and a point furthest from the plane. + // We can derive these points from the plane normal equation. + struct SPlaneBoxEdge + { + TRenderBoxEdge lowerEdge; + TRenderBoxEdge upperEdge; + }; + + struct SClipPlane + { + QT3DSVec3 normal; + QT3DSF32 d; + SPlaneBoxEdge mEdges; + + // For intersection tests, we only need to know if the numerator is greater than, equal to, + // or less than zero. + inline QT3DSF32 distance(const QT3DSVec3 &pt) const { return normal.dot(pt) + d; } + + // Only works if p0 is above the line and p1 is below the plane. + inline QT3DSVec3 intersectWithLine(const QT3DSVec3 &p0, const QT3DSVec3 &p1) const + { + QT3DSVec3 dir = p1 - p0; + QT3DSVec3 pointOnPlane = normal * (-d); +#ifdef _DEBUG + QT3DSF32 distanceOfPoint = distance(pointOnPlane); + QT3DS_ASSERT(NVAbs(distanceOfPoint) < 0.0001f); +#endif + QT3DSF32 numerator = (pointOnPlane - p0).dot(normal); + QT3DSF32 denominator = dir.dot(normal); + + QT3DS_ASSERT(NVAbs(denominator) > .0001f); + QT3DSF32 t = (numerator / denominator); + QT3DSVec3 retval = p0 + dir * t; +#ifdef _DEBUG + QT3DSF32 retvalDistance = distance(retval); + QT3DS_ASSERT(NVAbs(retvalDistance) < .0001f); +#endif + return retval; + } + + static inline QT3DSVec3 corner(const NVBounds3 &bounds, TRenderBoxEdge edge) + { + return QT3DSVec3((edge & BoxEdgeFlagValues::xMax) ? bounds.maximum[0] : bounds.minimum[0], + (edge & BoxEdgeFlagValues::yMax) ? bounds.maximum[1] : bounds.minimum[1], + (edge & BoxEdgeFlagValues::zMax) ? bounds.maximum[2] : bounds.minimum[2]); + } + + // dividing the distance numerator + + // I got this code from osg, but it is in graphics gems + // as well. + /** intersection test between plane and bounding sphere. + return 1 if the bs is completely above plane, + return 0 if the bs intersects the plane, + return -1 if the bs is completely below the plane.*/ + inline int intersect(const NVBounds3 &bounds) const + { + // if lowest point above plane than all above. + if (distance(corner(bounds, mEdges.lowerEdge)) > 0.0f) + return 1; + + // if highest point is below plane then all below. + if (distance(corner(bounds, mEdges.upperEdge)) < 0.0f) + return -1; + + // d_lower<=0.0f && d_upper>=0.0f + // therefore must be crossing plane. + return 0; + } + + inline void calculateBBoxEdges() + { + mEdges.upperEdge = TRenderBoxEdge( + static_cast<QT3DSU8>((normal[0] >= 0.0f ? BoxEdgeFlagValues::xMax : 0) + | (normal[1] >= 0.0f ? BoxEdgeFlagValues::yMax : 0) + | (normal[2] >= 0.0f ? BoxEdgeFlagValues::zMax : 0))); + + mEdges.lowerEdge = TRenderBoxEdge((~((QT3DSU8)mEdges.upperEdge)) & 7); + } + }; + + struct SClippingFrustum + { + SClipPlane mPlanes[6]; + + SClippingFrustum() {} + + SClippingFrustum(const QT3DSMat44 &modelviewprojection, SClipPlane nearPlane); + + bool intersectsWith(const NVBounds3 &bounds) const + { + for (QT3DSU32 idx = 0; idx < 6; ++idx) + if (mPlanes[idx].intersect(bounds) < 0) + return false; + return true; + } + + bool intersectsWith(const QT3DSVec3 &point, QT3DSF32 radius = 0.0f) const + { + for (QT3DSU32 idx = 0; idx < 6; ++idx) + if (mPlanes[idx].distance(point) < radius) + return false; + return true; + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderContextCore.cpp b/src/runtimerender/Qt3DSRenderContextCore.cpp new file mode 100644 index 0000000..33e1bf6 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderContextCore.cpp @@ -0,0 +1,858 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "EABase/eabase.h" //char16_t definition +#include "Qt3DSRenderContextCore.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "Qt3DSRenderEffectSystem.h" +#include "Qt3DSRenderShaderCache.h" +#include "foundation/Qt3DSFoundation.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "Qt3DSRenderCamera.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderThreadPool.h" +#include "Qt3DSRenderImageBatchLoader.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderPixelGraphicsRenderer.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "Qt3DSRenderBufferLoader.h" +#include "foundation/FastAllocator.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderPathManager.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "Qt3DSRenderCustomMaterialShaderGenerator.h" +#include "Qt3DSDistanceFieldRenderer.h" + +using namespace qt3ds::render; + +namespace { + +struct SRenderContextCore : public IQt3DSRenderContextCore +{ + NVFoundationBase &m_Foundation; + NVScopedRefCounted<IStringTable> m_StringTable; + NVScopedRefCounted<IPerfTimer> m_PerfTimer; + NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory; + NVScopedRefCounted<IThreadPool> m_ThreadPool; + NVScopedRefCounted<IDynamicObjectSystemCore> m_DynamicObjectSystem; + NVScopedRefCounted<ICustomMaterialSystemCore> m_MaterialSystem; + NVScopedRefCounted<IEffectSystemCore> m_EffectSystem; + NVScopedRefCounted<IBufferLoader> m_BufferLoader; + NVScopedRefCounted<IRenderPluginManagerCore> m_RenderPluginManagerCore; + NVScopedRefCounted<ITextRendererCore> m_TextRenderer; + NVScopedRefCounted<ITextRendererCore> m_OnscreenTexRenderer; + NVScopedRefCounted<IPathManagerCore> m_PathManagerCore; + NVScopedRefCounted<ITextRendererCore> m_distanceFieldRenderer; + + QT3DSI32 mRefCount; + SRenderContextCore(NVFoundationBase &fnd, IStringTable &strTable) + : m_Foundation(fnd) + , m_StringTable(strTable) + , m_PerfTimer(IPerfTimer::CreatePerfTimer(fnd)) + , m_InputStreamFactory(IInputStreamFactory::Create(fnd)) + , m_ThreadPool(IThreadPool::CreateThreadPool(fnd, 4)) + , mRefCount(0) + { + m_DynamicObjectSystem = IDynamicObjectSystemCore::CreateDynamicSystemCore(*this); + m_MaterialSystem = ICustomMaterialSystemCore::CreateCustomMaterialSystemCore(*this); + m_EffectSystem = IEffectSystemCore::CreateEffectSystemCore(*this); + m_RenderPluginManagerCore = + IRenderPluginManagerCore::Create(fnd, strTable, *m_InputStreamFactory); + m_BufferLoader = IBufferLoader::Create(m_Foundation, *m_InputStreamFactory, *m_ThreadPool); + m_PathManagerCore = IPathManagerCore::CreatePathManagerCore(*this); + } + + virtual ~SRenderContextCore() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + IStringTable &GetStringTable() override { return *m_StringTable; } + NVFoundationBase &GetFoundation() override { return m_Foundation; } + NVAllocatorCallback &GetAllocator() override { return m_Foundation.getAllocator(); } + IInputStreamFactory &GetInputStreamFactory() override { return *m_InputStreamFactory; } + IThreadPool &GetThreadPool() override { return *m_ThreadPool; } + IDynamicObjectSystemCore &GetDynamicObjectSystemCore() override + { + return *m_DynamicObjectSystem; + } + ICustomMaterialSystemCore &GetMaterialSystemCore() override { return *m_MaterialSystem; } + IEffectSystemCore &GetEffectSystemCore() override { return *m_EffectSystem; } + IPerfTimer &GetPerfTimer() override { return *m_PerfTimer; } + IBufferLoader &GetBufferLoader() override { return *m_BufferLoader; } + IRenderPluginManagerCore &GetRenderPluginCore() override { return *m_RenderPluginManagerCore; } + IPathManagerCore &GetPathManagerCore() override { return *m_PathManagerCore; } + IQt3DSRenderContext &CreateRenderContext(NVRenderContext &inContext, + const char8_t *inPrimitivesDirectory, + bool delayedLoading) 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; + } + ITextRendererCore *GetOnscreenTextRendererCore() override { return m_OnscreenTexRenderer.mPtr; } +}; + +inline float Clamp(float val, float inMin = 0.0f, float inMax = 1.0f) +{ + if (val < inMin) + return inMin; + if (val > inMax) + return inMax; + return val; +} + +struct SPerFrameAllocator : public NVAllocatorCallback +{ + SFastAllocator<> m_FastAllocator; + SSAutoDeallocatorAllocator m_LargeAllocator; + + SPerFrameAllocator(NVAllocatorCallback &baseAllocator) + : m_FastAllocator(baseAllocator, "PerFrameAllocation") + , m_LargeAllocator(baseAllocator) + { + } + + inline void *allocate(size_t inSize, const char *inFile, int inLine) + { + if (inSize < 8192) + return m_FastAllocator.allocate(inSize, "PerFrameAllocation", inFile, inLine, 0); + else + return m_LargeAllocator.allocate(inSize, "PerFrameAllocation", inFile, inLine, 0); + } + + inline void *allocate(size_t inSize, const char *inFile, int inLine, int, int) + { + if (inSize < 8192) + return m_FastAllocator.allocate(inSize, "PerFrameAllocation", inFile, inLine, 0); + else + return m_LargeAllocator.allocate(inSize, "PerFrameAllocation", inFile, inLine, 0); + } + + inline void deallocate(void *, size_t) {} + + void reset() + { + m_FastAllocator.reset(); + m_LargeAllocator.deallocateAllAllocations(); + } + + void *allocate(size_t inSize, const char *typeName, const char *inFile, int inLine, + int flags = 0) override + { + if (inSize < SFastAllocator<>::SlabSize) + return m_FastAllocator.allocate(inSize, typeName, inFile, inLine, flags); + else + return m_LargeAllocator.allocate(inSize, typeName, inFile, inLine, flags); + } + + void *allocate(size_t inSize, const char *typeName, const char *inFile, int inLine, + size_t alignment, size_t alignmentOffset) override + { + if (inSize < SFastAllocator<>::SlabSize) + return m_FastAllocator.allocate(inSize, typeName, inFile, inLine, alignment, + alignmentOffset); + else + return m_LargeAllocator.allocate(inSize, typeName, inFile, inLine, alignment, + alignmentOffset); + } + + void deallocate(void *) override {} +}; + +struct SRenderContext : public IQt3DSRenderContext +{ + NVScopedRefCounted<NVRenderContext> m_RenderContext; + NVScopedRefCounted<IQt3DSRenderContextCore> m_CoreContext; + NVScopedRefCounted<IStringTable> m_StringTable; + NVScopedRefCounted<IPerfTimer> m_PerfTimer; + NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory; + NVScopedRefCounted<IBufferManager> m_BufferManager; + NVScopedRefCounted<IResourceManager> m_ResourceManager; + 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; + NVScopedRefCounted<IDynamicObjectSystem> m_DynamicObjectSystem; + NVScopedRefCounted<IEffectSystem> m_EffectSystem; + NVScopedRefCounted<IShaderCache> m_ShaderCache; + NVScopedRefCounted<IThreadPool> m_ThreadPool; + NVScopedRefCounted<IImageBatchLoader> m_ImageBatchLoader; + NVScopedRefCounted<IRenderPluginManager> m_RenderPluginManager; + NVScopedRefCounted<ICustomMaterialSystem> m_CustomMaterialSystem; + NVScopedRefCounted<IPixelGraphicsRenderer> m_PixelGraphicsRenderer; + NVScopedRefCounted<IPathManager> m_PathManager; + NVScopedRefCounted<IShaderProgramGenerator> m_ShaderProgramGenerator; + NVScopedRefCounted<IDefaultMaterialShaderGenerator> m_DefaultMaterialShaderGenerator; + NVScopedRefCounted<ICustomMaterialShaderGenerator> m_CustomMaterialShaderGenerator; + SPerFrameAllocator m_PerFrameAllocator; + NVScopedRefCounted<IRenderList> m_RenderList; + QT3DSU32 m_FrameCount; + volatile QT3DSI32 mRefCount; + // Viewport that this render context should use + Option<NVRenderRect> m_Viewport; + QSize m_WindowDimensions; + ScaleModes::Enum m_ScaleMode; + bool m_WireframeMode; + bool m_IsInSubPresentation; + Option<QT3DSVec4> m_SceneColor; + Option<QT3DSVec4> m_MatteColor; + RenderRotationValues::Enum m_Rotation; + NVScopedRefCounted<NVRenderFrameBuffer> m_RotationFBO; + NVScopedRefCounted<NVRenderTexture2D> m_RotationTexture; + NVScopedRefCounted<NVRenderRenderBuffer> m_RotationDepthBuffer; + NVRenderFrameBuffer *m_ContextRenderTarget; + NVRenderRect m_PresentationViewport; + QSize m_PresentationDimensions; + QSize m_RenderPresentationDimensions; + QSize m_PreRenderPresentationDimensions; + QT3DSVec2 m_PresentationScale; + NVRenderRect m_VirtualViewport; + QPair<QT3DSF32, int> m_FPS; + bool m_AuthoringMode; + QVector<QT3DSF32> m_frameTimes; + + SRenderContext(NVRenderContext &ctx, IQt3DSRenderContextCore &inCore, + const char8_t *inApplicationDirectory, bool delayedLoading) + : m_RenderContext(ctx) + , m_CoreContext(inCore) + , m_StringTable(ctx.GetStringTable()) + , m_PerfTimer(inCore.GetPerfTimer()) + , m_InputStreamFactory(inCore.GetInputStreamFactory()) + , m_BufferManager( + IBufferManager::Create(ctx, *m_StringTable, *m_InputStreamFactory, *m_PerfTimer)) + , m_ResourceManager(IResourceManager::CreateResourceManager(ctx)) + , m_ShaderCache(IShaderCache::CreateShaderCache(ctx, *m_InputStreamFactory, *m_PerfTimer)) + , m_ThreadPool(inCore.GetThreadPool()) + , m_RenderList(IRenderList::CreateRenderList(ctx.GetFoundation())) + , m_PerFrameAllocator(ctx.GetAllocator()) + , m_FrameCount(0) + , mRefCount(0) + , m_WindowDimensions(800, 480) + , m_ScaleMode(ScaleModes::ExactSize) + , m_WireframeMode(false) + , m_IsInSubPresentation(false) + , m_Rotation(RenderRotationValues::NoRotation) + , m_ContextRenderTarget(NULL) + , m_PresentationScale(0, 0) + , m_FPS(qMakePair(0.0, 0)) + , m_AuthoringMode(false) + { + m_BufferManager->enableReloadableResources(delayedLoading); + m_OffscreenRenderManager = IOffscreenRenderManager::CreateOffscreenRenderManager( + ctx.GetAllocator(), *m_StringTable, *m_ResourceManager, *this); + m_Renderer = IQt3DSRenderer::CreateRenderer(*this); + if (inApplicationDirectory && *inApplicationDirectory) + m_InputStreamFactory->AddSearchDirectory(inApplicationDirectory); + + m_ImageBatchLoader = + IImageBatchLoader::CreateBatchLoader(ctx.GetFoundation(), *m_InputStreamFactory, + *m_BufferManager, *m_ThreadPool, *m_PerfTimer); + m_RenderPluginManager = inCore.GetRenderPluginCore().GetRenderPluginManager(ctx); + m_DynamicObjectSystem = inCore.GetDynamicObjectSystemCore().CreateDynamicSystem(*this); + m_EffectSystem = inCore.GetEffectSystemCore().GetEffectSystem(*this); + m_CustomMaterialSystem = inCore.GetMaterialSystemCore().GetCustomMaterialSystem(*this); + // as does the custom material system + m_PixelGraphicsRenderer = IPixelGraphicsRenderer::CreateRenderer(*this, *m_StringTable); + ITextRendererCore *theTextCore = inCore.GetTextRendererCore(); + m_ShaderProgramGenerator = IShaderProgramGenerator::CreateProgramGenerator(*this); + m_DefaultMaterialShaderGenerator = + IDefaultMaterialShaderGenerator::CreateDefaultMaterialShaderGenerator(*this); + m_CustomMaterialShaderGenerator = + ICustomMaterialShaderGenerator::CreateCustomMaterialShaderGenerator(*this); + if (theTextCore) { + m_TextRenderer = theTextCore->GetTextRenderer(ctx); + m_TextTextureCache = ITextTextureCache::CreateTextureCache( + 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); + m_TextTextureAtlas = ITextTextureAtlas::CreateTextureAtlas( + m_RenderContext->GetFoundation(), *m_OnscreenTextRenderer, *m_RenderContext); + } + m_PathManager = inCore.GetPathManagerCore().OnRenderSystemInitialize(*this); + +#if defined (QT3DS_SHADER_PLATFORM_LIBRARY_DIR) + const QString platformDirectory; +#if defined(_WIN32) + platformDirectory = QStringLiteral("res/platform/win"); +#elif defined(_LINUX) + platformDirectory = QStringLiteral("res/platform/linux"); +#elif defined(_MACOSX) + platformDirectory = QStringLiteral("res/platform/macos"); +#endif + GetDynamicObjectSystem().setShaderCodeLibraryPlatformDirectory(platformDirectory); +#endif + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext->GetAllocator()); + + IStringTable &GetStringTable() override { return *m_StringTable; } + NVFoundationBase &GetFoundation() override { return m_RenderContext->GetFoundation(); } + NVAllocatorCallback &GetAllocator() override { return m_RenderContext->GetAllocator(); } + IQt3DSRenderer &GetRenderer() override { return *m_Renderer; } + IBufferManager &GetBufferManager() override { return *m_BufferManager; } + IResourceManager &GetResourceManager() override { return *m_ResourceManager; } + NVRenderContext &GetRenderContext() override { return *m_RenderContext; } + IOffscreenRenderManager &GetOffscreenRenderManager() override + { + return *m_OffscreenRenderManager; + } + IInputStreamFactory &GetInputStreamFactory() override { return *m_InputStreamFactory; } + IEffectSystem &GetEffectSystem() override { return *m_EffectSystem; } + IShaderCache &GetShaderCache() override { return *m_ShaderCache; } + IThreadPool &GetThreadPool() override { return *m_ThreadPool; } + IImageBatchLoader &GetImageBatchLoader() override { return *m_ImageBatchLoader; } + ITextTextureCache *GetTextureCache() override { return m_TextTextureCache.mPtr; } + ITextTextureAtlas *GetTextureAtlas() override { return m_TextTextureAtlas.mPtr; } + IRenderPluginManager &GetRenderPluginManager() override { return *m_RenderPluginManager; } + IDynamicObjectSystem &GetDynamicObjectSystem() override { return *m_DynamicObjectSystem; } + ICustomMaterialSystem &GetCustomMaterialSystem() override { return *m_CustomMaterialSystem; } + IPixelGraphicsRenderer &GetPixelGraphicsRenderer() override { return *m_PixelGraphicsRenderer; } + IPerfTimer &GetPerfTimer() override { return *m_PerfTimer; } + IRenderList &GetRenderList() override { return *m_RenderList; } + IPathManager &GetPathManager() override { return *m_PathManager; } + IShaderProgramGenerator &GetShaderProgramGenerator() override + { + return *m_ShaderProgramGenerator; + } + IDefaultMaterialShaderGenerator &GetDefaultMaterialShaderGenerator() override + { + return *m_DefaultMaterialShaderGenerator; + } + ICustomMaterialShaderGenerator &GetCustomMaterialShaderGenerator() override + { + return *m_CustomMaterialShaderGenerator; + } + NVAllocatorCallback &GetPerFrameAllocator() override { return m_PerFrameAllocator; } + + QT3DSU32 GetFrameCount() override { return m_FrameCount; } + void SetFPS(QPair<QT3DSF32, int> inFPS) override { m_FPS = inFPS; } + QPair<QT3DSF32, int> GetFPS(void) override { return m_FPS; } + + void SetFrameTime(QT3DSF32 time) override + { + m_frameTimes.push_front(time); + // Store only one value for now. This can be increased once we have proper graph for + // the frame times. + if (m_frameTimes.size() > 1) + m_frameTimes.pop_back(); + } + QVector<QT3DSF32> GetFrameTimes() const override + { + return m_frameTimes; + } + + bool IsAuthoringMode() override { return m_AuthoringMode; } + void SetAuthoringMode(bool inMode) override { m_AuthoringMode = inMode; } + + bool IsInSubPresentation() override { return m_IsInSubPresentation; } + void SetInSubPresentation(bool inValue) override { m_IsInSubPresentation = inValue; } + + 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; } + void SetMatteColor(Option<QT3DSVec4> inMatteColor) override { m_MatteColor = inMatteColor; } + + void SetWindowDimensions(const QSize &inWindowDimensions) override + { + m_WindowDimensions = inWindowDimensions; + } + + QSize GetWindowDimensions() override { return m_WindowDimensions; } + + void SetScaleMode(ScaleModes::Enum inMode) override { m_ScaleMode = inMode; } + + ScaleModes::Enum GetScaleMode() override { return m_ScaleMode; } + + void SetWireframeMode(bool inEnable) override { m_WireframeMode = inEnable; } + + bool GetWireframeMode() override { return m_WireframeMode; } + + void SetViewport(Option<NVRenderRect> inViewport) override { m_Viewport = inViewport; } + Option<NVRenderRect> GetViewport() const override { return m_Viewport; } + + IRenderWidgetContext &GetRenderWidgetContext() override + { + return m_Renderer->GetRenderWidgetContext(); + } + + eastl::pair<NVRenderRect, NVRenderRect> GetPresentationViewportAndOuterViewport() const + { + QSize thePresentationDimensions(m_PresentationDimensions); + NVRenderRect theOuterViewport(GetContextViewport()); + if (m_Rotation == RenderRotationValues::Clockwise90 + || m_Rotation == RenderRotationValues::Clockwise270) { + eastl::swap(theOuterViewport.m_Width, theOuterViewport.m_Height); + eastl::swap(theOuterViewport.m_X, theOuterViewport.m_Y); + } + // Calculate the presentation viewport perhaps with the window width and height swapped. + return eastl::make_pair( + GetPresentationViewport(theOuterViewport, m_ScaleMode, thePresentationDimensions), + theOuterViewport); + } + + NVRenderRectF GetDisplayViewport() const override + { + return GetPresentationViewportAndOuterViewport().first; + } + + void SetPresentationDimensions(const QSize &inPresentationDimensions) override + { + m_PresentationDimensions = inPresentationDimensions; + } + QSize GetCurrentPresentationDimensions() const override + { + return m_PresentationDimensions; + } + + void SetRenderRotation(RenderRotationValues::Enum inRotation) override + { + m_Rotation = inRotation; + } + + RenderRotationValues::Enum GetRenderRotation() const override { return m_Rotation; } + QT3DSVec2 GetMousePickViewport() const override + { + bool renderOffscreen = m_Rotation != RenderRotationValues::NoRotation; + if (renderOffscreen) + return QT3DSVec2((QT3DSF32)m_PresentationViewport.m_Width, + (QT3DSF32)m_PresentationViewport.m_Height); + else + return QT3DSVec2((QT3DSF32)m_WindowDimensions.width(), (QT3DSF32)m_WindowDimensions.height()); + } + NVRenderRect GetContextViewport() const override + { + NVRenderRect retval; + if (m_Viewport.hasValue()) + retval = *m_Viewport; + else + retval = NVRenderRect(0, 0, m_WindowDimensions.width(), m_WindowDimensions.height()); + + return retval; + } + + QT3DSVec2 GetMousePickMouseCoords(const QT3DSVec2 &inMouseCoords) const override + { + bool renderOffscreen = m_Rotation != RenderRotationValues::NoRotation; + if (renderOffscreen) { + QSize thePresentationDimensions(m_RenderPresentationDimensions); + NVRenderRect theViewport(GetContextViewport()); + // Calculate the presentation viewport perhaps with the presentation width and height + // swapped. + NVRenderRect thePresentationViewport = + GetPresentationViewport(theViewport, m_ScaleMode, thePresentationDimensions); + // Translate pick into presentation space without rotations or anything else. + QT3DSF32 YHeightDiff = (QT3DSF32)((QT3DSF32)m_WindowDimensions.height() + - (QT3DSF32)thePresentationViewport.m_Height); + QT3DSVec2 theLocalMouse((inMouseCoords.x - thePresentationViewport.m_X), + (inMouseCoords.y - YHeightDiff + thePresentationViewport.m_Y)); + switch (m_Rotation) { + default: + case RenderRotationValues::NoRotation: + QT3DS_ASSERT(false); + break; + case RenderRotationValues::Clockwise90: + eastl::swap(theLocalMouse.x, theLocalMouse.y); + theLocalMouse.y = thePresentationViewport.m_Width - theLocalMouse.y; + break; + case RenderRotationValues::Clockwise180: + theLocalMouse.y = thePresentationViewport.m_Height - theLocalMouse.y; + theLocalMouse.x = thePresentationViewport.m_Width - theLocalMouse.x; + break; + case RenderRotationValues::Clockwise270: + eastl::swap(theLocalMouse.x, theLocalMouse.y); + theLocalMouse.x = thePresentationViewport.m_Height - theLocalMouse.x; + break; + } + return theLocalMouse; + } + return inMouseCoords; + } + + NVRenderRect GetPresentationViewport(const NVRenderRect &inViewerViewport, + ScaleModes::Enum inScaleToFit, + const QSize &inPresDimensions) const + { + NVRenderRect retval; + QT3DSI32 theWidth = inViewerViewport.m_Width; + QT3DSI32 theHeight = inViewerViewport.m_Height; + if (inPresDimensions.width() == 0 || inPresDimensions.height() == 0) + return NVRenderRect(0, 0, 0, 0); + // Setup presentation viewport. This may or may not match the physical viewport that we + // want to setup. + // Avoiding scaling keeps things as sharp as possible. + if (inScaleToFit == ScaleModes::ExactSize) { + retval.m_Width = inPresDimensions.width(); + retval.m_Height = inPresDimensions.height(); + retval.m_X = (theWidth - (QT3DSI32)inPresDimensions.width()) / 2; + retval.m_Y = (theHeight - (QT3DSI32)inPresDimensions.height()) / 2; + } else if (inScaleToFit == ScaleModes::ScaleToFit + || inScaleToFit == ScaleModes::FitSelected) { + // Scale down in such a way to preserve aspect ratio. + float screenAspect = (float)theWidth / (float)theHeight; + float thePresentationAspect = + (float)inPresDimensions.width() / (float)inPresDimensions.height(); + if (screenAspect >= thePresentationAspect) { + // if the screen height is the limiting factor + retval.m_Y = 0; + retval.m_Height = theHeight; + retval.m_Width = (QT3DSI32)(thePresentationAspect * retval.m_Height); + retval.m_X = (theWidth - retval.m_Width) / 2; + } else { + retval.m_X = 0; + retval.m_Width = theWidth; + retval.m_Height = (QT3DSI32)(retval.m_Width / thePresentationAspect); + retval.m_Y = (theHeight - retval.m_Height) / 2; + } + } else { + // Setup the viewport for everything and let the presentations figure it out. + retval.m_X = 0; + retval.m_Y = 0; + retval.m_Width = theWidth; + retval.m_Height = theHeight; + } + retval.m_X += inViewerViewport.m_X; + retval.m_Y += inViewerViewport.m_Y; + return retval; + } + + void RenderText2D(QT3DSF32 x, QT3DSF32 y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) override + { + m_Renderer->RenderText2D(x, y, inColor, text); + } + + void RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) override + { + m_Renderer->RenderGpuProfilerStats(x, y, inColor); + } + + NVRenderRect GetPresentationViewport() const override { return m_PresentationViewport; } + struct SBeginFrameResult + { + bool m_RenderOffscreen; + QSize m_PresentationDimensions; + bool m_ScissorTestEnabled; + NVRenderRect m_ScissorRect; + NVRenderRect m_Viewport; + QSize m_FBODimensions; + SBeginFrameResult(bool ro, QSize presDims, bool scissorEnabled, + NVRenderRect scissorRect, NVRenderRect viewport, + QSize fboDims) + : m_RenderOffscreen(ro) + , m_PresentationDimensions(presDims) + , m_ScissorTestEnabled(scissorEnabled) + , m_ScissorRect(scissorRect) + , m_Viewport(viewport) + , m_FBODimensions(fboDims) + { + } + SBeginFrameResult() {} + }; + + // Calculated values passed from beginframe to setupRenderTarget. + // Trying to avoid duplicate code as much as possible. + SBeginFrameResult m_BeginFrameResult; + + void BeginFrame(bool firstFrame) override + { + m_PreRenderPresentationDimensions = m_PresentationDimensions; + QSize thePresentationDimensions(m_PreRenderPresentationDimensions); + NVRenderRect theContextViewport(GetContextViewport()); + m_PerFrameAllocator.reset(); + IRenderList &theRenderList(*m_RenderList); + theRenderList.BeginFrame(); + if (m_Viewport.hasValue()) { + theRenderList.SetScissorTestEnabled(true); + theRenderList.SetScissorRect(theContextViewport); + } else { + theRenderList.SetScissorTestEnabled(false); + } + bool renderOffscreen = m_Rotation != RenderRotationValues::NoRotation; + eastl::pair<NVRenderRect, NVRenderRect> thePresViewportAndOuterViewport = + GetPresentationViewportAndOuterViewport(); + NVRenderRect theOuterViewport = thePresViewportAndOuterViewport.second; + // Calculate the presentation viewport perhaps with the window width and height swapped. + NVRenderRect thePresentationViewport = thePresViewportAndOuterViewport.first; + m_PresentationViewport = thePresentationViewport; + m_PresentationScale = QT3DSVec2( + (QT3DSF32)thePresentationViewport.m_Width / (QT3DSF32)thePresentationDimensions.width(), + (QT3DSF32)thePresentationViewport.m_Height / (QT3DSF32)thePresentationDimensions.height()); + QSize fboDimensions; + if (thePresentationViewport.m_Width > 0 && thePresentationViewport.m_Height > 0) { + if (renderOffscreen == false) { + m_PresentationDimensions = QSize(thePresentationViewport.m_Width, + thePresentationViewport.m_Height); + m_RenderList->SetViewport(thePresentationViewport); + if (thePresentationViewport.m_X || thePresentationViewport.m_Y + || thePresentationViewport.m_Width != (QT3DSI32)theOuterViewport.m_Width + || thePresentationViewport.m_Height != (QT3DSI32)theOuterViewport.m_Height) { + m_RenderList->SetScissorRect(thePresentationViewport); + m_RenderList->SetScissorTestEnabled(true); + } + } else { + QT3DSU32 imageWidth = ITextRenderer::NextMultipleOf4(thePresentationViewport.m_Width); + QT3DSU32 imageHeight = + ITextRenderer::NextMultipleOf4(thePresentationViewport.m_Height); + fboDimensions = QSize(imageWidth, imageHeight); + m_PresentationDimensions = QSize(thePresentationViewport.m_Width, + thePresentationViewport.m_Height); + NVRenderRect theSceneViewport = NVRenderRect(0, 0, imageWidth, imageHeight); + m_RenderList->SetScissorTestEnabled(false); + m_RenderList->SetViewport(theSceneViewport); + } + } + + m_BeginFrameResult = SBeginFrameResult( + renderOffscreen, m_PresentationDimensions, m_RenderList->IsScissorTestEnabled(), + m_RenderList->GetScissor(), m_RenderList->GetViewport(), fboDimensions); + + m_Renderer->BeginFrame(); + m_OffscreenRenderManager->BeginFrame(); + if (m_TextRenderer) + m_TextRenderer->BeginFrame(); + if (m_TextTextureCache) + m_TextTextureCache->BeginFrame(); + m_ImageBatchLoader->BeginFrame(firstFrame); + } + + QT3DSVec2 GetPresentationScaleFactor() const override { return m_PresentationScale; } + + virtual void SetupRenderTarget() + { + NVRenderRect theContextViewport(GetContextViewport()); + if (m_Viewport.hasValue()) { + m_RenderContext->SetScissorTestEnabled(true); + m_RenderContext->SetScissorRect(theContextViewport); + } else { + m_RenderContext->SetScissorTestEnabled(false); + } + { + QT3DSVec4 theClearColor; + if (m_MatteColor.hasValue()) + theClearColor = m_MatteColor; + else + theClearColor = m_SceneColor; + m_RenderContext->SetClearColor(theClearColor); + m_RenderContext->Clear(qt3ds::render::NVRenderClearValues::Color); + } + bool renderOffscreen = m_BeginFrameResult.m_RenderOffscreen; + m_RenderContext->SetViewport(m_BeginFrameResult.m_Viewport); + m_RenderContext->SetScissorRect(m_BeginFrameResult.m_ScissorRect); + m_RenderContext->SetScissorTestEnabled(m_BeginFrameResult.m_ScissorTestEnabled); + + if (m_PresentationViewport.m_Width > 0 && m_PresentationViewport.m_Height > 0) { + if (renderOffscreen == false) { + if (m_RotationFBO != NULL) { + m_ResourceManager->Release(*m_RotationFBO); + m_ResourceManager->Release(*m_RotationTexture); + m_ResourceManager->Release(*m_RotationDepthBuffer); + m_RotationFBO = NULL; + m_RotationTexture = NULL; + m_RotationDepthBuffer = NULL; + } + if (m_SceneColor.hasValue() && m_SceneColor.getValue().w != 0.0f) { + m_RenderContext->SetClearColor(m_SceneColor); + m_RenderContext->Clear(qt3ds::render::NVRenderClearValues::Color); + } + } else { + QT3DSU32 imageWidth = m_BeginFrameResult.m_FBODimensions.width(); + QT3DSU32 imageHeight = m_BeginFrameResult.m_FBODimensions.height(); + NVRenderTextureFormats::Enum theColorBufferFormat = NVRenderTextureFormats::RGBA8; + NVRenderRenderBufferFormats::Enum theDepthBufferFormat = + NVRenderRenderBufferFormats::Depth16; + m_ContextRenderTarget = m_RenderContext->GetRenderTarget(); + if (m_RotationFBO == NULL) { + m_RotationFBO = m_ResourceManager->AllocateFrameBuffer(); + m_RotationTexture = m_ResourceManager->AllocateTexture2D( + imageWidth, imageHeight, theColorBufferFormat); + m_RotationDepthBuffer = m_ResourceManager->AllocateRenderBuffer( + imageWidth, imageHeight, theDepthBufferFormat); + m_RotationFBO->Attach(NVRenderFrameBufferAttachments::Color0, + *m_RotationTexture); + m_RotationFBO->Attach(NVRenderFrameBufferAttachments::Depth, + *m_RotationDepthBuffer); + } else { + STextureDetails theDetails = m_RotationTexture->GetTextureDetails(); + if (theDetails.m_Width != imageWidth || theDetails.m_Height != imageHeight) { + m_RotationTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, imageWidth, + imageHeight, theColorBufferFormat); + m_RotationDepthBuffer->SetDimensions( + qt3ds::render::NVRenderRenderBufferDimensions(imageWidth, imageHeight)); + } + } + m_RenderContext->SetRenderTarget(m_RotationFBO); + if (m_SceneColor.hasValue()) { + m_RenderContext->SetClearColor(m_SceneColor); + m_RenderContext->Clear(qt3ds::render::NVRenderClearValues::Color); + } + } + } + } + + void RunRenderTasks() override + { + m_RenderList->RunRenderTasks(); + SetupRenderTarget(); + } + + // Note this runs before EndFrame + virtual void TeardownRenderTarget() + { + if (m_RotationFBO) { + ScaleModes::Enum theScaleToFit = m_ScaleMode; + NVRenderRect theOuterViewport(GetContextViewport()); + m_RenderContext->SetRenderTarget(m_ContextRenderTarget); + QSize thePresentationDimensions = GetCurrentPresentationDimensions(); + if (m_Rotation == RenderRotationValues::Clockwise90 + || m_Rotation == RenderRotationValues::Clockwise270) { + thePresentationDimensions = QSize(thePresentationDimensions.height(), + thePresentationDimensions.width()); + } + m_RenderPresentationDimensions = thePresentationDimensions; + // Calculate the presentation viewport perhaps with the presentation width and height + // swapped. + NVRenderRect thePresentationViewport = + GetPresentationViewport(theOuterViewport, theScaleToFit, thePresentationDimensions); + SCamera theCamera; + switch (m_Rotation) { + default: + QT3DS_ASSERT(false); + break; + case RenderRotationValues::Clockwise90: + theCamera.m_Rotation.z = 90; + break; + case RenderRotationValues::Clockwise180: + theCamera.m_Rotation.z = 180; + break; + case RenderRotationValues::Clockwise270: + theCamera.m_Rotation.z = 270; + break; + } + TORAD(theCamera.m_Rotation.z); + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + m_RenderContext->SetViewport(thePresentationViewport); + QT3DSVec2 theCameraDimensions((QT3DSF32)thePresentationViewport.m_Width, + (QT3DSF32)thePresentationViewport.m_Height); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, (QT3DSU32)thePresentationViewport.m_Width, + (QT3DSU32)thePresentationViewport.m_Height), + theCameraDimensions); + QT3DSMat44 theVP; + theCamera.CalculateViewProjectionMatrix(theVP); + SNode theTempNode; + theTempNode.CalculateGlobalVariables(); + QT3DSMat44 theMVP; + QT3DSMat33 theNormalMat; + theTempNode.CalculateMVPAndNormalMatrix(theVP, theMVP, theNormalMat); + m_RenderContext->SetCullingEnabled(false); + m_RenderContext->SetBlendingEnabled(false); + m_RenderContext->SetDepthTestEnabled(false); + m_Renderer->RenderQuad(QT3DSVec2((QT3DSF32)m_PresentationViewport.m_Width, + (QT3DSF32)m_PresentationViewport.m_Height), + theMVP, *m_RotationTexture); + } + } + + void EndFrame() override + { + TeardownRenderTarget(); + m_ImageBatchLoader->EndFrame(); + if (m_TextTextureCache) + m_TextTextureCache->EndFrame(); + if (m_TextRenderer) + m_TextRenderer->EndFrame(); + if (m_distanceFieldRenderer) + m_distanceFieldRenderer->EndFrame(); + m_OffscreenRenderManager->EndFrame(); + m_Renderer->EndFrame(); + m_CustomMaterialSystem->EndFrame(); + m_PresentationDimensions = m_PreRenderPresentationDimensions; + ++m_FrameCount; + } +}; + +IQt3DSRenderContext &SRenderContextCore::CreateRenderContext(NVRenderContext &inContext, + const char8_t *inPrimitivesDirectory, + bool delayedLoading) +{ + return *QT3DS_NEW(m_Foundation.getAllocator(), SRenderContext)(inContext, *this, + inPrimitivesDirectory, + delayedLoading); +} +} + +IQt3DSRenderContextCore &IQt3DSRenderContextCore::Create(NVFoundationBase &fnd, IStringTable &strt) +{ + return *QT3DS_NEW(fnd.getAllocator(), SRenderContextCore)(fnd, strt); +} diff --git a/src/runtimerender/Qt3DSRenderContextCore.h b/src/runtimerender/Qt3DSRenderContextCore.h new file mode 100644 index 0000000..302571d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderContextCore.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CONTEXT_CORE_H +#define QT3DS_RENDER_CONTEXT_CORE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderPresentation.h" + +#include <QtCore/qpair.h> +#include <QtCore/qsize.h> + +namespace qt3ds { +namespace render { + struct ScaleModes + { + enum Enum { + ExactSize = 0, // Ensure the viewport is exactly same size as application + ScaleToFit = 1, // Resize viewport keeping aspect ratio + ScaleToFill = 2, // Resize viewport to entire window + FitSelected = 3, // Resize presentation to fit into viewport + }; + }; + + // Part of render context that does not require the render system. + class IQt3DSRenderContextCore : public NVRefCounted + { + public: + virtual IStringTable &GetStringTable() = 0; + virtual NVFoundationBase &GetFoundation() = 0; + virtual NVAllocatorCallback &GetAllocator() = 0; + virtual IInputStreamFactory &GetInputStreamFactory() = 0; + virtual IThreadPool &GetThreadPool() = 0; + virtual IDynamicObjectSystemCore &GetDynamicObjectSystemCore() = 0; + virtual ICustomMaterialSystemCore &GetMaterialSystemCore() = 0; + virtual IEffectSystemCore &GetEffectSystemCore() = 0; + virtual IPerfTimer &GetPerfTimer() = 0; + virtual IBufferLoader &GetBufferLoader() = 0; + virtual IRenderPluginManagerCore &GetRenderPluginCore() = 0; + virtual IPathManagerCore &GetPathManagerCore() = 0; + // 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; + // The render context maintains a reference to this object. + virtual IQt3DSRenderContext &CreateRenderContext(NVRenderContext &inContext, + const char8_t *inPrimitivesDirectory, + bool delayedLoading) = 0; + + static IQt3DSRenderContextCore &Create(NVFoundationBase &fnd, IStringTable &strt); + }; + + class IQt3DSRenderContext : public NVRefCounted + { + protected: + virtual ~IQt3DSRenderContext() {} + public: + virtual IStringTable &GetStringTable() = 0; + virtual NVFoundationBase &GetFoundation() = 0; + virtual NVAllocatorCallback &GetAllocator() = 0; + virtual IQt3DSRenderer &GetRenderer() = 0; + virtual IRenderWidgetContext &GetRenderWidgetContext() = 0; + virtual IBufferManager &GetBufferManager() = 0; + virtual IResourceManager &GetResourceManager() = 0; + virtual NVRenderContext &GetRenderContext() = 0; + virtual IOffscreenRenderManager &GetOffscreenRenderManager() = 0; + virtual IInputStreamFactory &GetInputStreamFactory() = 0; + virtual IEffectSystem &GetEffectSystem() = 0; + virtual IShaderCache &GetShaderCache() = 0; + virtual IThreadPool &GetThreadPool() = 0; + virtual IImageBatchLoader &GetImageBatchLoader() = 0; + virtual IRenderPluginManager &GetRenderPluginManager() = 0; + virtual IDynamicObjectSystem &GetDynamicObjectSystem() = 0; + virtual ICustomMaterialSystem &GetCustomMaterialSystem() = 0; + virtual IPixelGraphicsRenderer &GetPixelGraphicsRenderer() = 0; + 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; + virtual IDefaultMaterialShaderGenerator &GetDefaultMaterialShaderGenerator() = 0; + virtual ICustomMaterialShaderGenerator &GetCustomMaterialShaderGenerator() = 0; + // The memory used for the per frame allocator is released as the first step in BeginFrame. + // This is useful for short lived objects and datastructures. + virtual NVAllocatorCallback &GetPerFrameAllocator() = 0; + // Get the number of times EndFrame has been called + virtual QT3DSU32 GetFrameCount() = 0; + + // Get fps + virtual QPair<QT3DSF32, int> GetFPS() = 0; + // Set fps by higher level, etc application + virtual void SetFPS(QPair<QT3DSF32, int> inFPS) = 0; + virtual void SetFrameTime(QT3DSF32 time) = 0; + virtual QVector<QT3DSF32> GetFrameTimes() const = 0; + + // Currently there are a few things that need to work differently + // in authoring mode vs. runtime. The particle effects, for instance + // need to be framerate-independent at runtime but framerate-dependent during + // authoring time assuming virtual 16 ms frames. + // Defaults to falst. + virtual bool IsAuthoringMode() = 0; + virtual void SetAuthoringMode(bool inMode) = 0; + + // This one is setup by the runtime binding + virtual ITextRenderer *GetOnscreenTextRenderer() = 0; + virtual ITextTextureAtlas *GetTextureAtlas() = 0; + + // Sub presentations change the rendering somewhat. + virtual bool IsInSubPresentation() = 0; + virtual void SetInSubPresentation(bool inValue) = 0; + virtual void SetSceneColor(Option<QT3DSVec4> inSceneColor) = 0; + virtual void SetMatteColor(Option<QT3DSVec4> inMatteColor) = 0; + + // Render screen aligned 2D text at x,y + virtual void RenderText2D(QT3DSF32 x, QT3DSF32 y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) = 0; + // render Gpu profiler values + virtual void RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) = 0; + + // The reason you can set both window dimensions and an overall viewport is that the mouse + // needs to be inverted + // which requires the window height, and then the rest of the system really requires the + // viewport. + virtual void SetWindowDimensions(const QSize &inWindowDimensions) = 0; + virtual QSize GetWindowDimensions() = 0; + + // In addition to the window dimensions which really have to be set, you can optionally + // set the viewport which will force the entire viewer to render specifically to this + // viewport. + virtual void SetViewport(Option<NVRenderRect> inViewport) = 0; + virtual Option<NVRenderRect> GetViewport() const = 0; + virtual NVRenderRect GetContextViewport() const = 0; + // Only valid between calls to Begin,End. + virtual NVRenderRect GetPresentationViewport() const = 0; + + virtual void SetScaleMode(ScaleModes::Enum inMode) = 0; + virtual ScaleModes::Enum GetScaleMode() = 0; + + virtual void SetWireframeMode(bool inEnable) = 0; + virtual bool GetWireframeMode() = 0; + + // Return the viewport the system is using to render data to. This gives the the dimensions + // of the rendered system. It is dependent on but not equal to the viewport. + virtual NVRenderRectF GetDisplayViewport() const = 0; + + // Layers require the current presentation dimensions in order to render. + virtual void + SetPresentationDimensions(const QSize &inPresentationDimensions) = 0; + virtual QSize GetCurrentPresentationDimensions() const = 0; + + virtual void SetRenderRotation(RenderRotationValues::Enum inRotation) = 0; + virtual RenderRotationValues::Enum GetRenderRotation() const = 0; + + virtual QT3DSVec2 GetMousePickViewport() const = 0; + virtual QT3DSVec2 GetMousePickMouseCoords(const QT3DSVec2 &inMouseCoords) const = 0; + + // Valid during and just after prepare for render. + virtual QT3DSVec2 GetPresentationScaleFactor() const = 0; + + // Steps needed to render: + // 1. BeginFrame - sets up new target in render graph + // 2. Add everything you need to the render graph + // 3. RunRenderGraph - runs the graph, rendering things to main render target + // 4. Render any additional stuff to main render target on top of previously rendered + // information + // 5. EndFrame + + // Clients need to call this every frame in order for various subsystems to release + // temporary per-frame allocated objects. + // Also sets up the viewport according to SetViewportInfo + // and the topmost presentation dimensions. Expects there to be exactly one presentation + // dimension pushed at this point. + // This also starts a render target in the render graph. + virtual void BeginFrame(bool firstFrame) = 0; + + // This runs through the added tasks in reverse order. This is used to render dependencies + // before rendering to the main render target. + virtual void RunRenderTasks() = 0; + // Now you can render to the main render target if you want to render over the top + // of everything. + // Next call end frame. + virtual void EndFrame() = 0; + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderCustomMaterialRenderContext.h b/src/runtimerender/Qt3DSRenderCustomMaterialRenderContext.h new file mode 100644 index 0000000..5283660 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderCustomMaterialRenderContext.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CUSTOM_MATERIAL_RENDER_CONTEXT_H +#define QT3DS_RENDER_CUSTOM_MATERIAL_RENDER_CONTEXT_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSMat44.h" +#include "foundation/Qt3DSMat33.h" +#include "Qt3DSRenderShaderKeys.h" + +namespace qt3ds { +namespace render { + + struct SLayerRenderData; + + struct SCustomMaterialRenderContext + { + // The lights and camera will not change per layer, + // so that information can be set once for all the shaders. + const SLayer &m_Layer; + const SLayerRenderData &m_LayerData; + NVDataRef<SLight *> m_Lights; + const SCamera &m_Camera; + + // Per-object information. + const SModel &m_Model; + const SRenderSubset &m_Subset; + const QT3DSMat44 &m_ModelViewProjection; + const QT3DSMat44 &m_ModelMatrix; ///< model to world transformation + const QT3DSMat33 &m_NormalMatrix; + const SCustomMaterial &m_Material; + const NVRenderTexture2D *m_DepthTexture; + const NVRenderTexture2D *m_AOTexture; + SShaderDefaultMaterialKey m_MaterialKey; + SRenderableImage *m_FirstImage; + QT3DSF32 m_Opacity; + + SCustomMaterialRenderContext( + const SLayer &layer, const SLayerRenderData &data, NVDataRef<SLight *> lights, + const SCamera &cam, const SModel &m, const SRenderSubset &subset, const QT3DSMat44 &mvp, + const QT3DSMat44 &world, const QT3DSMat33 &nm, const SCustomMaterial &material, + const NVRenderTexture2D *depthTex, const NVRenderTexture2D *aoTex, + SShaderDefaultMaterialKey inMaterialKey, SRenderableImage *inFirstImage = NULL, + QT3DSF32 opacity = 1.0) + : m_Layer(layer) + , m_LayerData(data) + , m_Lights(lights) + , m_Camera(cam) + , m_Model(m) + , m_Subset(subset) + , m_ModelViewProjection(mvp) + , m_ModelMatrix(world) + , m_NormalMatrix(nm) + , m_Material(material) + , m_DepthTexture(depthTex) + , m_AOTexture(aoTex) + , m_MaterialKey(inMaterialKey) + , m_FirstImage(inFirstImage) + , m_Opacity(opacity) + { + } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp b/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp new file mode 100644 index 0000000..62205ec --- /dev/null +++ b/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp @@ -0,0 +1,1265 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderCustomMaterialShaderGenerator.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSRenderImage.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShadowMap.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderLightConstantProperties.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +namespace { +struct SShaderLightProperties +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + RenderLightTypes::Enum m_LightType; + SLightSourceShader m_LightData; + volatile QT3DSI32 mRefCount; + + SShaderLightProperties(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_LightType(RenderLightTypes::Directional) + , mRefCount(0) + { + } + + void Set(const SLight *inLight) + { + QT3DSVec3 dir(0, 0, 1); + if (inLight->m_LightType == RenderLightTypes::Directional) { + dir = inLight->GetScalingCorrectDirection(); + // we lit in world sapce + dir *= -1; + m_LightData.m_position = QT3DSVec4(dir, 0.0); + } else if (inLight->m_LightType == RenderLightTypes::Area) { + dir = inLight->GetScalingCorrectDirection(); + m_LightData.m_position = QT3DSVec4(inLight->GetGlobalPos(), 1.0); + } else { + dir = inLight->GetGlobalPos(); + m_LightData.m_position = QT3DSVec4(dir, 1.0); + } + + m_LightType = inLight->m_LightType; + + m_LightData.m_direction = QT3DSVec4(dir, 0.0); + + float normalizedBrightness = inLight->m_Brightness / 100.0f; + m_LightData.m_diffuse = QT3DSVec4(inLight->m_DiffuseColor.getXYZ() * normalizedBrightness, + inLight->m_DiffuseColor.w); + m_LightData.m_specular = QT3DSVec4(inLight->m_SpecularColor.getXYZ() * normalizedBrightness, + inLight->m_DiffuseColor.w); + + if (inLight->m_LightType == RenderLightTypes::Area) { + m_LightData.m_width = inLight->m_AreaWidth; + m_LightData.m_height = inLight->m_AreaWidth; + + QT3DSMat33 theDirMatrix(inLight->m_GlobalTransform.getUpper3x3()); + m_LightData.m_right = + QT3DSVec4(theDirMatrix.transform(QT3DSVec3(1, 0, 0)), inLight->m_AreaWidth); + m_LightData.m_up = + QT3DSVec4(theDirMatrix.transform(QT3DSVec3(0, 1, 0)), inLight->m_AreaHeight); + } else { + m_LightData.m_width = 0.0; + m_LightData.m_height = 0.0; + m_LightData.m_right = QT3DSVec4(0.0f); + m_LightData.m_up = QT3DSVec4(0.0f); + + // These components only apply to CG lights + m_LightData.m_ambient = inLight->m_AmbientColor; + + m_LightData.m_constantAttenuation = 1.0; + m_LightData.m_linearAttenuation = inLight->m_LinearFade; + m_LightData.m_quadraticAttenuation = inLight->m_ExponentialFade; + m_LightData.m_spotCutoff = 180.0; + } + + if (m_LightType == RenderLightTypes::Point) { + m_LightData.m_shadowView = QT3DSMat44::createIdentity(); + } else { + m_LightData.m_shadowView = inLight->m_GlobalTransform; + } + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + static SShaderLightProperties CreateLightEntry(NVRenderShaderProgram &inShader) + { + return SShaderLightProperties(inShader); + } +}; + +/** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ +struct SShaderTextureProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + { + } + SShaderTextureProperties() {} +}; + +/* We setup some shared state on the custom material shaders */ +struct SShaderGeneratorGeneratedShader +{ + typedef nvhash_map<QT3DSU32, SShaderTextureProperties> TCustomMaterialImagMap; + + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + // Specific properties we know the shader has to have. + NVRenderCachedShaderProperty<QT3DSMat44> m_ModelMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewProjMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSMat33> m_NormalMatrix; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPos; + NVRenderCachedShaderProperty<QT3DSMat44> m_ProjMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewportMatrix; + NVRenderCachedShaderProperty<QT3DSVec2> m_CamProperties; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AOTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeProps; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOpts; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeRot; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOfs; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe2; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbe2Props; + NVRenderCachedShaderProperty<QT3DSI32> m_LightCount; + NVRenderCachedShaderProperty<QT3DSI32> m_AreaLightCount; + NVRenderCachedShaderProperty<QT3DSI32> m_ShadowMapCount; + NVRenderCachedShaderProperty<QT3DSI32> m_ShadowCubeCount; + NVRenderCachedShaderProperty<QT3DSF32> m_Opacity; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_LightsBuffer; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AreaLightsBuffer; + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_lightsProperties; + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_areaLightsProperties; + + typedef NVRenderCachedShaderPropertyArray<NVRenderTexture2D *, + QT3DS_MAX_NUM_SHADOWS> ShadowMapPropertyArray; + typedef NVRenderCachedShaderPropertyArray<NVRenderTextureCube *, + QT3DS_MAX_NUM_SHADOWS> ShadowCubePropertyArray; + + ShadowMapPropertyArray m_shadowMaps; + ShadowCubePropertyArray m_shadowCubes; + + // Cache the image property name lookups + TCustomMaterialImagMap m_Images; // Images external to custom material usage + volatile QT3DSI32 m_RefCount; + + SShaderGeneratorGeneratedShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_ModelMatrix("model_matrix", inShader) + , m_ViewProjMatrix("model_view_projection", inShader) + , m_ViewMatrix("view_matrix", inShader) + , m_NormalMatrix("normal_matrix", inShader) + , m_CameraPos("camera_position", inShader) + , m_ProjMatrix("view_projection_matrix", inShader) + , m_ViewportMatrix("viewport_matrix", inShader) + , m_CamProperties("camera_properties", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_AOTexture("ao_sampler", inShader) + , m_LightProbe("light_probe", inShader) + , m_LightProbeProps("light_probe_props", inShader) + , m_LightProbeOpts("light_probe_opts", inShader) + , m_LightProbeRot("light_probe_rotation", inShader) + , m_LightProbeOfs("light_probe_offset", inShader) + , m_LightProbe2("light_probe2", inShader) + , m_LightProbe2Props("light_probe2_props", inShader) + , m_LightCount("uNumLights", inShader) + , m_AreaLightCount("uNumAreaLights", inShader) + , m_ShadowMapCount("uNumShadowMaps", inShader) + , m_ShadowCubeCount("uNumShadowCubes", inShader) + , m_Opacity("object_opacity", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_LightsBuffer("cbBufferLights", inShader) + , m_AreaLightsBuffer("cbBufferAreaLights", inShader) + , m_lightsProperties(nullptr) + , m_areaLightsProperties(nullptr) + , m_shadowMaps("shadowMaps[0]", inShader) + , m_shadowCubes("shadowCubes[0]", inShader) + , m_Images(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Images") + , m_RefCount(0) + { + m_Shader.addRef(); + } + + ~SShaderGeneratorGeneratedShader() + { + m_Shader.release(); + delete m_lightsProperties; + delete m_areaLightsProperties; + } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetLightProperties(int count) + { + if (!m_lightsProperties || m_areaLightsProperties->m_lightCountInt < count) { + if (m_lightsProperties) + delete m_lightsProperties; + m_lightsProperties = new SLightConstantProperties<SShaderGeneratorGeneratedShader> + ("lights", "uNumLights", *this, false, count); + } + return m_lightsProperties; + } + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetAreaLightProperties(int count) + { + if (!m_areaLightsProperties || m_areaLightsProperties->m_lightCountInt < count) { + if (m_areaLightsProperties) + delete m_areaLightsProperties; + m_areaLightsProperties = new SLightConstantProperties<SShaderGeneratorGeneratedShader> + ("areaLights", "uNumAreaLights", *this, false, count); + } + return m_areaLightsProperties; + } +}; + +struct SShaderGenerator : public ICustomMaterialShaderGenerator +{ + typedef Qt3DSString TStrType; + typedef nvhash_map<NVRenderShaderProgram *, NVScopedRefCounted<SShaderGeneratorGeneratedShader>> + TProgramToShaderMap; + typedef eastl::pair<size_t, NVScopedRefCounted<SShaderLightProperties>> + TCustomMaterialLightEntry; + typedef eastl::pair<size_t, NVRenderCachedShaderProperty<NVRenderTexture2D *>> TShadowMapEntry; + typedef eastl::pair<size_t, NVRenderCachedShaderProperty<NVRenderTextureCube *>> + TShadowCubeEntry; + typedef qt3ds::foundation::nvhash_map<CRegisteredString, + NVScopedRefCounted<qt3ds::render::NVRenderConstantBuffer>> + TStrConstanBufMap; + + IQt3DSRenderContext &m_RenderContext; + IShaderProgramGenerator &m_ProgramGenerator; + + const SCustomMaterial *m_CurrentMaterial; + SShaderDefaultMaterialKey *m_CurrentKey; + IDefaultMaterialVertexPipeline *m_CurrentPipeline; + TShaderFeatureSet m_CurrentFeatureSet; + NVDataRef<SLight *> m_Lights; + SRenderableImage *m_FirstImage; + bool m_HasTransparency; + + TStrType m_ImageStem; + TStrType m_ImageSampler; + TStrType m_ImageFragCoords; + TStrType m_ImageRotScale; + TStrType m_ImageOffset; + + eastl::string m_GeneratedShaderString; + + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + TProgramToShaderMap m_ProgramToShaderMap; + + nvvector<TCustomMaterialLightEntry> m_LightEntries; + + TStrConstanBufMap m_ConstantBuffers; ///< store all constants buffers + + QT3DSI32 m_RefCount; + + SShaderGenerator(IQt3DSRenderContext &inRc) + : m_RenderContext(inRc) + , m_ProgramGenerator(m_RenderContext.GetShaderProgramGenerator()) + , m_CurrentMaterial(NULL) + , m_CurrentKey(NULL) + , m_CurrentPipeline(NULL) + , m_FirstImage(NULL) + , m_HasTransparency(false) + , m_ProgramToShaderMap(inRc.GetAllocator(), "m_ProgramToShaderMap") + , m_LightEntries(inRc.GetAllocator(), "m_LightEntries") + , m_ConstantBuffers(inRc.GetAllocator(), "m_ConstantBuffers") + , m_RefCount(0) + { + } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + m_ConstantBuffers.clear(); + NVDelete(m_RenderContext.GetAllocator(), this); + } + } + + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IDefaultMaterialVertexPipeline &VertexGenerator() { return *m_CurrentPipeline; } + IShaderStageGenerator &FragmentGenerator() + { + return *m_ProgramGenerator.GetStage(ShaderGeneratorStages::Fragment); + } + SShaderDefaultMaterialKey &Key() { return *m_CurrentKey; } + const SCustomMaterial &Material() { return *m_CurrentMaterial; } + TShaderFeatureSet FeatureSet() { return m_CurrentFeatureSet; } + bool HasTransparency() { return m_HasTransparency; } + + QT3DSU32 + ConvertTextureTypeValue(ImageMapTypes::Enum inType) + { + NVRenderTextureTypeValue::Enum retVal = NVRenderTextureTypeValue::Unknown; + + switch (inType) { + case ImageMapTypes::LightmapIndirect: + retVal = NVRenderTextureTypeValue::LightmapIndirect; + break; + case ImageMapTypes::LightmapRadiosity: + retVal = NVRenderTextureTypeValue::LightmapRadiosity; + break; + case ImageMapTypes::LightmapShadow: + retVal = NVRenderTextureTypeValue::LightmapShadow; + break; + case ImageMapTypes::Bump: + retVal = NVRenderTextureTypeValue::Bump; + break; + case ImageMapTypes::Diffuse: + retVal = NVRenderTextureTypeValue::Diffuse; + break; + case ImageMapTypes::Displacement: + retVal = NVRenderTextureTypeValue::Displace; + break; + default: + retVal = NVRenderTextureTypeValue::Unknown; + break; + } + + QT3DS_ASSERT(retVal != NVRenderTextureTypeValue::Unknown); + + return (QT3DSU32)retVal; + } + + SImageVariableNames GetImageVariableNames(QT3DSU32 imageIdx) override + { + // convert to NVRenderTextureTypeValue + NVRenderTextureTypeValue::Enum texType = (NVRenderTextureTypeValue::Enum)imageIdx; + m_ImageStem.assign(NVRenderTextureTypeValue::toString(texType)); + m_ImageStem.append("_"); + m_ImageSampler = m_ImageStem; + m_ImageSampler.append("sampler"); + m_ImageFragCoords = m_ImageStem; + m_ImageFragCoords.append("uv_coords"); + m_ImageRotScale = m_ImageStem; + m_ImageRotScale.append("rot_scale"); + m_ImageOffset = m_ImageStem; + m_ImageOffset.append("offset"); + + SImageVariableNames retVal; + retVal.m_ImageSampler = m_ImageSampler.c_str(); + retVal.m_ImageFragCoords = m_ImageFragCoords.c_str(); + return retVal; + } + + void SetImageShaderVariables(SShaderGeneratorGeneratedShader &inShader, + SRenderableImage &inImage) + { + // skip displacement and emissive mask maps which are handled differently + if (inImage.m_MapType == ImageMapTypes::Displacement + || inImage.m_MapType == ImageMapTypes::Emissive) + return; + + SShaderGeneratorGeneratedShader::TCustomMaterialImagMap::iterator iter = + inShader.m_Images.find(inImage.m_MapType); + if (iter == inShader.m_Images.end()) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(inImage.m_MapType)); + inShader.m_Images.insert(eastl::make_pair( + (QT3DSU32)inImage.m_MapType, + SShaderTextureProperties(names.m_ImageSampler, m_ImageOffset.c_str(), + m_ImageRotScale.c_str(), inShader.m_Shader))); + iter = inShader.m_Images.find(inImage.m_MapType); + } + + SShaderTextureProperties &theShaderProps = iter->second; + const QT3DSMat44 &textureTransform = inImage.m_Image.m_TextureTransform; + const QT3DSF32 *dataPtr(textureTransform.front()); + QT3DSVec3 offsets(dataPtr[12], dataPtr[13], 0.0f); + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + // The image horizontal and vertical tiling modes need to be set here, before we set texture + // on the shader. + // because setting the image on the texture forces the textue to bind and immediately apply + // any tex params. + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapS( + inImage.m_Image.m_HorizontalTilingMode); + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapT( + inImage.m_Image.m_VerticalTilingMode); + + theShaderProps.m_Sampler.Set(inImage.m_Image.m_TextureData.m_Texture); + theShaderProps.m_Offsets.Set(offsets); + theShaderProps.m_Rotations.Set(rotations); + } + + void GenerateImageUVCoordinates(IShaderStageGenerator &, QT3DSU32, QT3DSU32, SRenderableImage &) override {} + + ///< get the light constant buffer and generate if necessary + NVRenderConstantBuffer *GetLightConstantBuffer(const char *name, QT3DSU32 inLightCount) + { + NVRenderContext &theContext(m_RenderContext.GetRenderContext()); + + // we assume constant buffer support + QT3DS_ASSERT(theContext.GetConstantBufferSupport()); + // we only create if if we have lights + if (!inLightCount || !theContext.GetConstantBufferSupport()) + return NULL; + + CRegisteredString theName = theContext.GetStringTable().RegisterStr(name); + NVRenderConstantBuffer *pCB = theContext.GetConstantBuffer(theName); + + if (!pCB) { + // create with size of all structures + int for light count + SLightSourceShader s[QT3DS_MAX_NUM_LIGHTS]; + NVDataRef<QT3DSU8> cBuffer((QT3DSU8 *)&s, (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + + (4 * sizeof(QT3DSI32))); + pCB = theContext.CreateConstantBuffer( + name, qt3ds::render::NVRenderBufferUsageType::Static, + (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + (4 * sizeof(QT3DSI32)), cBuffer); + if (!pCB) { + QT3DS_ASSERT(false); + return NULL; + } + // init first set + memset(&s[0], 0x0, sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS); + QT3DSI32 cgLights[4] = {0, 0, 0, 0}; + pCB->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32) * 4)); + pCB->UpdateRaw(4 * sizeof(QT3DSI32), + NVDataRef<QT3DSU8>((QT3DSU8 *)&s[0], + sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS)); + pCB->Update(); // update to hardware + + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + } + + return pCB; + } + + bool GenerateVertexShader(SShaderDefaultMaterialKey &, const char8_t *inShaderPathName) + { + qt3ds::render::IDynamicObjectSystem &theDynamicSystem( + m_RenderContext.GetDynamicObjectSystem()); + Qt3DSString theShaderBuffer; + theDynamicSystem.GetShaderSource( + m_RenderContext.GetStringTable().RegisterStr(inShaderPathName), theShaderBuffer); + + eastl::string srcString(theShaderBuffer.c_str()); + + // Check if the vertex shader portion already contains a main function + // The same string contains both the vertex and the fragment shader + // The last "#ifdef FRAGMENT_SHADER" should mark the start of the fragment shader + eastl_size_t fragmentDefStart = srcString.find("#ifdef FRAGMENT_SHADER"); + eastl_size_t nextIndex = fragmentDefStart; + while (nextIndex != eastl::string::npos) { + nextIndex = srcString.find("#ifdef FRAGMENT_SHADER", nextIndex + 1); + if (nextIndex != eastl::string::npos) + fragmentDefStart = nextIndex; + } + eastl_size_t mainStart = srcString.find("void main()"); + + if (mainStart != eastl::string::npos && (fragmentDefStart == eastl::string::npos + || mainStart < fragmentDefStart)) { + TShaderGeneratorStageFlags stages(IShaderProgramGenerator::DefaultFlags()); + ProgramGenerator().BeginProgram(stages); + IDefaultMaterialVertexPipeline &vertexShader(VertexGenerator()); + vertexShader << "#define VERTEX_SHADER\n\n"; + vertexShader << srcString.data() << Endl; + return true; + } + + // vertex displacement + QT3DSU32 imageIdx = 0; + SRenderableImage *displacementImage = NULL; + QT3DSU32 displacementImageIdx = 0; + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + if (img->m_MapType == ImageMapTypes::Displacement) { + displacementImage = img; + displacementImageIdx = imageIdx; + break; + } + } + + // the pipeline opens/closes up the shaders stages + VertexGenerator().BeginVertexGeneration(displacementImageIdx, displacementImage); + return false; + } + + SShaderGeneratorGeneratedShader &GetShaderForProgram(NVRenderShaderProgram &inProgram) + { + eastl::pair<TProgramToShaderMap::iterator, bool> inserter = + m_ProgramToShaderMap.insert(eastl::make_pair( + &inProgram, NVScopedRefCounted<SShaderGeneratorGeneratedShader>(NULL))); + if (inserter.second) { + NVAllocatorCallback &alloc(m_RenderContext.GetRenderContext().GetAllocator()); + inserter.first->second = QT3DS_NEW(alloc, SShaderGeneratorGeneratedShader)( + inProgram, m_RenderContext.GetRenderContext()); + } + return *inserter.first->second; + } + + virtual SShaderLightProperties *SetLight(NVRenderShaderProgram &inShader, size_t lightIdx, + size_t shadeIdx, const SLight *inLight, + SShadowMapEntry *inShadow, QT3DSI32 shadowIdx, + QT3DSF32 shadowDist) + { + SShaderLightProperties *theLightEntry(NULL); + for (QT3DSU32 idx = 0, end = m_LightEntries.size(); idx < end && theLightEntry == NULL; + ++idx) { + if (m_LightEntries[idx].first == lightIdx + && m_LightEntries[idx].second->m_Shader.mPtr == &inShader + && m_LightEntries[idx].second->m_LightType == inLight->m_LightType) { + theLightEntry = m_LightEntries[idx].second; + } + } + if (theLightEntry == NULL) { + // create a new name + eastl::string lightName; + if (inLight->m_LightType == RenderLightTypes::Area) + lightName = "arealights"; + else + lightName = "lights"; + char buf[16]; + _snprintf(buf, 16, "[%d]", int(shadeIdx)); + lightName.append(buf); + + NVScopedRefCounted<SShaderLightProperties> theNewEntry = + QT3DS_NEW(m_RenderContext.GetAllocator(), + SShaderLightProperties)(SShaderLightProperties::CreateLightEntry(inShader)); + m_LightEntries.push_back(eastl::make_pair(lightIdx, theNewEntry)); + theLightEntry = theNewEntry.mPtr; + } + theLightEntry->Set(inLight); + theLightEntry->m_LightData.m_shadowControls = + QT3DSVec4(inLight->m_ShadowBias, inLight->m_ShadowFactor, shadowDist, 0.0); + theLightEntry->m_LightData.m_shadowIdx = (inShadow) ? shadowIdx : -1; + + return theLightEntry; + } + + void SetShadowMaps(NVRenderShaderProgram &inProgram, SShadowMapEntry *inShadow, + QT3DSI32 &numShadowMaps, QT3DSI32 &numShadowCubes, bool shadowMap, + SShaderGeneratorGeneratedShader::ShadowMapPropertyArray &shadowMaps, + SShaderGeneratorGeneratedShader::ShadowCubePropertyArray &shadowCubes) + { + Q_UNUSED(inProgram) + if (inShadow) { + if (shadowMap == false && inShadow->m_DepthCube + && (numShadowCubes < QT3DS_MAX_NUM_SHADOWS)) { + shadowCubes.m_array[numShadowCubes] = inShadow->m_DepthCube.mPtr; + ++numShadowCubes; + } else if (shadowMap && inShadow->m_DepthMap + && (numShadowMaps < QT3DS_MAX_NUM_SHADOWS)) { + shadowMaps.m_array[numShadowMaps] = inShadow->m_DepthMap.mPtr; + ++numShadowMaps; + } + } + } + + void SetGlobalProperties(NVRenderShaderProgram &inProgram, const SLayer & /*inLayer*/ + , + SCamera &inCamera, QT3DSVec3, NVDataRef<SLight *> inLights, + NVDataRef<QT3DSVec3>, Qt3DSShadowMap *inShadowMaps) + { + SShaderGeneratorGeneratedShader &theShader(GetShaderForProgram(inProgram)); + m_RenderContext.GetRenderContext().SetActiveShader(&inProgram); + + SCamera &theCamera(inCamera); + + QT3DSVec2 camProps(theCamera.m_ClipNear, theCamera.m_ClipFar); + theShader.m_CamProperties.Set(camProps); + theShader.m_CameraPos.Set(theCamera.GetGlobalPos()); + + if (theShader.m_ViewMatrix.IsValid()) + theShader.m_ViewMatrix.Set(theCamera.m_GlobalTransform.getInverse()); + + if (theShader.m_ProjMatrix.IsValid()) { + QT3DSMat44 vProjMat; + inCamera.CalculateViewProjectionMatrix(vProjMat); + theShader.m_ProjMatrix.Set(vProjMat); + } + + // set lights separate for area lights + QT3DSI32 cgLights = 0, areaLights = 0; + QT3DSI32 numShadowMaps = 0, numShadowCubes = 0; + + // this call setup the constant buffer for ambient occlusion and shadow + theShader.m_AoShadowParams.Set(); + + if (m_RenderContext.GetRenderContext().GetConstantBufferSupport()) { + NVRenderConstantBuffer *pLightCb = + GetLightConstantBuffer("cbBufferLights", inLights.size()); + NVRenderConstantBuffer *pAreaLightCb = + GetLightConstantBuffer("cbBufferAreaLights", inLights.size()); + + // Split the count between CG lights and area lights + for (QT3DSU32 lightIdx = 0; lightIdx < inLights.size() && pLightCb; ++lightIdx) { + SShadowMapEntry *theShadow = NULL; + if (inShadowMaps && inLights[lightIdx]->m_CastShadow) + theShadow = inShadowMaps->GetShadowMapEntry(lightIdx); + + QT3DSI32 shdwIdx = (inLights[lightIdx]->m_LightType + != RenderLightTypes::Directional) + ? numShadowCubes + : numShadowMaps; + SetShadowMaps(inProgram, theShadow, numShadowMaps, numShadowCubes, + inLights[lightIdx]->m_LightType == RenderLightTypes::Directional, + theShader.m_shadowMaps, theShader.m_shadowCubes); + + if (inLights[lightIdx]->m_LightType == RenderLightTypes::Area) { + SShaderLightProperties *theAreaLightEntry = + SetLight(inProgram, lightIdx, areaLights, inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + + if (theAreaLightEntry && pAreaLightCb) { + pAreaLightCb->UpdateRaw( + areaLights * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&theAreaLightEntry->m_LightData, + sizeof(SLightSourceShader))); + } + + areaLights++; + } else { + SShaderLightProperties *theLightEntry = + SetLight(inProgram, lightIdx, cgLights, inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + + if (theLightEntry && pLightCb) { + pLightCb->UpdateRaw( + cgLights * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&theLightEntry->m_LightData, + sizeof(SLightSourceShader))); + } + + cgLights++; + } + } + + if (pLightCb) { + pLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + theShader.m_LightsBuffer.Set(); + } + if (pAreaLightCb) { + pAreaLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&areaLights, + sizeof(QT3DSI32))); + theShader.m_AreaLightsBuffer.Set(); + } + + theShader.m_LightCount.Set(cgLights); + theShader.m_AreaLightCount.Set(areaLights); + } else { + QVector<SShaderLightProperties *> lprop; + QVector<SShaderLightProperties *> alprop; + for (QT3DSU32 lightIdx = 0; lightIdx < inLights.size(); ++lightIdx) { + + SShadowMapEntry *theShadow = NULL; + if (inShadowMaps && inLights[lightIdx]->m_CastShadow) + theShadow = inShadowMaps->GetShadowMapEntry(lightIdx); + + QT3DSI32 shdwIdx = (inLights[lightIdx]->m_LightType + != RenderLightTypes::Directional) + ? numShadowCubes + : numShadowMaps; + SetShadowMaps(inProgram, theShadow, numShadowMaps, numShadowCubes, + inLights[lightIdx]->m_LightType == RenderLightTypes::Directional, + theShader.m_shadowMaps, theShader.m_shadowCubes); + + SShaderLightProperties *p = SetLight(inProgram, lightIdx, areaLights, + inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + if (inLights[lightIdx]->m_LightType == RenderLightTypes::Area) + alprop.push_back(p); + else + lprop.push_back(p); + } + SLightConstantProperties<SShaderGeneratorGeneratedShader> *lightProperties + = theShader.GetLightProperties(lprop.size()); + SLightConstantProperties<SShaderGeneratorGeneratedShader> *areaLightProperties + = theShader.GetAreaLightProperties(alprop.size()); + + lightProperties->updateLights(lprop); + areaLightProperties->updateLights(alprop); + + theShader.m_LightCount.Set(lprop.size()); + theShader.m_AreaLightCount.Set(alprop.size()); + } + for (int i = numShadowMaps; i < QT3DS_MAX_NUM_SHADOWS; ++i) + theShader.m_shadowMaps.m_array[i] = NULL; + for (int i = numShadowCubes; i < QT3DS_MAX_NUM_SHADOWS; ++i) + theShader.m_shadowCubes.m_array[i] = NULL; + theShader.m_shadowMaps.Set(numShadowMaps); + theShader.m_shadowCubes.Set(numShadowCubes); + theShader.m_ShadowMapCount.Set(numShadowMaps); + theShader.m_ShadowCubeCount.Set(numShadowCubes); + } + + void SetMaterialProperties(NVRenderShaderProgram &inProgram, const SCustomMaterial &inMaterial, + const QT3DSVec2 &, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + NVRenderTexture2D *inDepthTexture, NVRenderTexture2D *inSSaoTexture, + SImage *inLightProbe, SImage *inLightProbe2, QT3DSF32 inProbeHorizon, + QT3DSF32 inProbeBright, QT3DSF32 inProbe2Window, QT3DSF32 inProbe2Pos, + QT3DSF32 inProbe2Fade, QT3DSF32 inProbeFOV) + { + ICustomMaterialSystem &theMaterialSystem(m_RenderContext.GetCustomMaterialSystem()); + SShaderGeneratorGeneratedShader &theShader(GetShaderForProgram(inProgram)); + + theShader.m_ViewProjMatrix.Set(inModelViewProjection); + theShader.m_NormalMatrix.Set(inNormalMatrix); + theShader.m_ModelMatrix.Set(inGlobalTransform); + + theShader.m_DepthTexture.Set(inDepthTexture); + theShader.m_AOTexture.Set(inSSaoTexture); + + theShader.m_Opacity.Set(inOpacity); + + qt3ds::render::SImage *theLightProbe = inLightProbe; + qt3ds::render::SImage *theLightProbe2 = inLightProbe2; + + if (inMaterial.m_IblProbe && inMaterial.m_IblProbe->m_TextureData.m_Texture) { + theLightProbe = inMaterial.m_IblProbe; + } + + if (theLightProbe) { + if (theLightProbe->m_TextureData.m_Texture) { + NVRenderTextureCoordOp::Enum theHorzLightProbeTilingMode = + theLightProbe->m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum theVertLightProbeTilingMode = + theLightProbe->m_VerticalTilingMode; + theLightProbe->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + + const QT3DSMat44 &textureTransform = theLightProbe->m_TextureTransform; + // We separate rotational information from offset information so that just maybe the + // shader + // will attempt to push less information to the card. + const QT3DSF32 *dataPtr(textureTransform.front()); + // The third member of the offsets contains a flag indicating if the texture was + // premultiplied or not. + // We use this to mix the texture alpha. + // light_probe_offsets.w is now no longer being used to enable/disable fast IBL, + // (it's now the only option) + // So now, it's storing the number of mip levels in the IBL image. + QT3DSVec4 offsets(dataPtr[12], dataPtr[13], + theLightProbe->m_TextureData.m_TextureFlags.IsPreMultiplied() ? 1.0f + : 0.0f, + (float)theLightProbe->m_TextureData.m_Texture->GetNumMipmaps()); + // Fast IBL is always on; + // inRenderContext.m_Layer.m_FastIbl ? 1.0f : 0.0f ); + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + theShader.m_LightProbeRot.Set(rotations); + theShader.m_LightProbeOfs.Set(offsets); + + if ((!inMaterial.m_IblProbe) && (inProbeFOV < 180.f)) { + theShader.m_LightProbeOpts.Set( + QT3DSVec4(0.01745329251994329547f * inProbeFOV, 0.0f, 0.0f, 0.0f)); + } + + // Also make sure to add the secondary texture, but it should only be added if the + // primary + // (i.e. background) texture is also there. + if (theLightProbe2 && theLightProbe2->m_TextureData.m_Texture) { + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + theShader.m_LightProbe2.Set(theLightProbe2->m_TextureData.m_Texture); + theShader.m_LightProbe2Props.Set( + QT3DSVec4(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f)); + + const QT3DSMat44 &xform2 = theLightProbe2->m_TextureTransform; + const QT3DSF32 *dataPtr(xform2.front()); + + theShader.m_LightProbeProps.Set( + QT3DSVec4(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f)); + } else { + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + theShader.m_LightProbeProps.Set( + QT3DSVec4(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f)); + } + } else { + theShader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + theShader.m_LightProbe.Set(theLightProbe->m_TextureData.m_Texture); + + } else { + theShader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + // finally apply custom material shader properties + theMaterialSystem.ApplyShaderPropertyValues(inMaterial, inProgram); + + // additional textures + for (SRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_NextImage) + SetImageShaderVariables(theShader, *theImage); + } + + void SetMaterialProperties(NVRenderShaderProgram &inProgram, + const SGraphObject &inMaterial, const QT3DSVec2 &inCameraVec, + const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, + const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override + { + const SCustomMaterial &theCustomMaterial( + reinterpret_cast<const SCustomMaterial &>(inMaterial)); + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::CustomMaterial); + + SetGlobalProperties(inProgram, inRenderProperties.m_Layer, inRenderProperties.m_Camera, + inRenderProperties.m_CameraDirection, inRenderProperties.m_Lights, + inRenderProperties.m_LightDirections, + inRenderProperties.m_ShadowMapManager); + + SetMaterialProperties(inProgram, theCustomMaterial, inCameraVec, inModelViewProjection, + inNormalMatrix, inGlobalTransform, inFirstImage, inOpacity, + inRenderProperties.m_DepthTexture, inRenderProperties.m_SSaoTexture, + inRenderProperties.m_LightProbe, inRenderProperties.m_LightProbe2, + inRenderProperties.m_ProbeHorizon, inRenderProperties.m_ProbeBright, + inRenderProperties.m_Probe2Window, inRenderProperties.m_Probe2Pos, + inRenderProperties.m_Probe2Fade, inRenderProperties.m_ProbeFOV); + } + + void GenerateLightmapIndirectFunc(IShaderStageGenerator &inFragmentShader, + SImage *pEmissiveLightmap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialLightmapIndirect()\n{\n"; + inFragmentShader << " vec4 indirect = vec4( 0.0, 0.0, 0.0, 0.0 );\n"; + if (pEmissiveLightmap) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(ImageMapTypes::LightmapIndirect)); + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n indirect = evalIndirectLightmap( " << m_ImageSampler + << ", varTexCoord1, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return indirect.rgb;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapRadiosityFunc(IShaderStageGenerator &inFragmentShader, + SImage *pRadiosityLightmap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialLightmapRadiosity()\n{\n"; + inFragmentShader << " vec4 radiosity = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; + if (pRadiosityLightmap) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(ImageMapTypes::LightmapRadiosity)); + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n radiosity = evalRadiosityLightmap( " << m_ImageSampler + << ", varTexCoord1, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return radiosity.rgb;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapShadowFunc(IShaderStageGenerator &inFragmentShader, + SImage *pBakedShadowMap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec4 computeMaterialLightmapShadow()\n{\n"; + inFragmentShader << " vec4 shadowMask = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; + if (pBakedShadowMap) { + SImageVariableNames names = + GetImageVariableNames((QT3DSU32)NVRenderTextureTypeValue::LightmapShadow); + // Add uniforms + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n shadowMask = evalShadowLightmap( " << m_ImageSampler + << ", texCoord0, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return shadowMask;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapIndirectSetupCode(IShaderStageGenerator &inFragmentShader, + SRenderableImage *pIndirectLightmap, + SRenderableImage *pRadiosityLightmap) + { + if (!pIndirectLightmap && !pRadiosityLightmap) + return; + + eastl::string finalValue; + + inFragmentShader << "\n"; + inFragmentShader << "void initializeLayerVariablesWithLightmap(void)\n{\n"; + if (pIndirectLightmap) { + inFragmentShader + << " vec3 lightmapIndirectValue = computeMaterialLightmapIndirect( );\n"; + finalValue.append("vec4(lightmapIndirectValue, 1.0)"); + } + if (pRadiosityLightmap) { + inFragmentShader + << " vec3 lightmapRadisoityValue = computeMaterialLightmapRadiosity( );\n"; + if (finalValue.empty()) + finalValue.append("vec4(lightmapRadisoityValue, 1.0)"); + else + finalValue.append(" + vec4(lightmapRadisoityValue, 1.0)"); + } + + finalValue.append(";\n"); + + char buf[16]; + for (QT3DSU32 idx = 0; idx < Material().m_LayerCount; idx++) { + _snprintf(buf, 16, "[%d]", idx); + inFragmentShader << " layers" << buf << ".base += " << finalValue.c_str(); + inFragmentShader << " layers" << buf << ".layer += " << finalValue.c_str(); + } + + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapShadowCode(IShaderStageGenerator &inFragmentShader, + SRenderableImage *pBakedShadowMap) + { + if (pBakedShadowMap) { + inFragmentShader << " tmpShadowTerm *= computeMaterialLightmapShadow( );\n\n"; + } + } + + void ApplyEmissiveMask(IShaderStageGenerator &inFragmentShader, SImage *pEmissiveMaskMap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialEmissiveMask()\n{\n"; + inFragmentShader << " vec3 emissiveMask = vec3( 1.0, 1.0, 1.0 );\n"; + if (pEmissiveMaskMap) { + inFragmentShader << " texture_coordinate_info tci;\n"; + inFragmentShader << " texture_coordinate_info transformed_tci;\n"; + inFragmentShader << " tci = textureCoordinateInfo( texCoord0, tangent, binormal );\n"; + inFragmentShader << " transformed_tci = transformCoordinate( " + "rotationTranslationScale( vec3( 0.000000, 0.000000, 0.000000 ), "; + inFragmentShader << "vec3( 0.000000, 0.000000, 0.000000 ), vec3( 1.000000, 1.000000, " + "1.000000 ) ), tci );\n"; + inFragmentShader << " emissiveMask = fileTexture( " + << pEmissiveMaskMap->m_ImageShaderName.c_str() + << ", vec3( 0, 0, 0 ), vec3( 1, 1, 1 ), mono_alpha, transformed_tci, "; + inFragmentShader << "vec2( 0.000000, 1.000000 ), vec2( 0.000000, 1.000000 ), " + "wrap_repeat, wrap_repeat, gamma_default ).tint;\n"; + } + + inFragmentShader << " return emissiveMask;\n"; + inFragmentShader << "}\n\n"; + } + + // Returns true if custom shader provides main function + bool GenerateFragmentShader(SShaderDefaultMaterialKey &, const char8_t *inShaderPathName, + bool hasCustomVertexShader) + { + qt3ds::render::IDynamicObjectSystem &theDynamicSystem( + m_RenderContext.GetDynamicObjectSystem()); + Qt3DSString theShaderBuffer; + theDynamicSystem.GetShaderSource( + m_RenderContext.GetStringTable().RegisterStr(inShaderPathName), theShaderBuffer); + + QT3DS_ASSERT(theShaderBuffer.size() > 0); + + // light maps + bool hasLightmaps = false; + SRenderableImage *lightmapShadowImage = NULL; + SRenderableImage *lightmapIndirectImage = NULL; + SRenderableImage *lightmapRadisoityImage = NULL; + + for (SRenderableImage *img = m_FirstImage; img != NULL; img = img->m_NextImage) { + if (img->m_MapType == ImageMapTypes::LightmapIndirect) { + lightmapIndirectImage = img; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapRadiosity) { + lightmapRadisoityImage = img; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapShadow) { + lightmapShadowImage = img; + } + } + + if (!hasCustomVertexShader) { + VertexGenerator().GenerateUVCoords(0); + // for lightmaps we expect a second set of uv coordinates + if (hasLightmaps) + VertexGenerator().GenerateUVCoords(1); + } + + IDefaultMaterialVertexPipeline &vertexShader(VertexGenerator()); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + + eastl::string srcString(theShaderBuffer.c_str()); + + if (m_RenderContext.GetRenderContext().GetRenderContextType() + == NVRenderContextValues::GLES2) { + eastl::string::size_type pos = 0; + while ((pos = srcString.find("out vec4 fragColor", pos)) != eastl::string::npos) { + srcString.insert(pos, "//"); + pos += int(strlen("//out vec4 fragColor")); + } + } + + fragmentShader << "#define FRAGMENT_SHADER\n\n"; + + // Check if the fragment shader portion already contains a main function + // The same string contains both the vertex and the fragment shader + // The last "#ifdef FRAGMENT_SHADER" should mark the start of the fragment shader + eastl_size_t fragmentDefStart = srcString.find("#ifdef FRAGMENT_SHADER"); + eastl_size_t nextIndex = fragmentDefStart; + while (nextIndex != eastl::string::npos) { + nextIndex = srcString.find("#ifdef FRAGMENT_SHADER", nextIndex + 1); + if (nextIndex != eastl::string::npos) + fragmentDefStart = nextIndex; + } + if (fragmentDefStart == eastl::string::npos) + return false; + + eastl_size_t mainStart = srcString.find("void main()"); + if (mainStart != eastl::string::npos && mainStart < fragmentDefStart) + mainStart = srcString.find("void main()", mainStart + 1); + + bool hasCustomFragmentShader = mainStart != eastl::string::npos; + + if (!hasCustomFragmentShader) + fragmentShader.AddInclude("evalLightmaps.glsllib"); + + // check dielectric materials + if (!Material().IsDielectric()) + fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 1\n\n"; + else + fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 0\n\n"; + + fragmentShader << "#define QT3DS_ENABLE_RNM 0\n\n"; + + fragmentShader << srcString.data() << Endl; + + if (hasCustomFragmentShader) { + fragmentShader << "#define FRAGMENT_SHADER\n\n"; + if (!hasCustomVertexShader) { + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateVarTangentAndBinormal(); + vertexShader.GenerateWorldPosition(); + + vertexShader.GenerateViewVector(); + } + return true; + } + + if (Material().HasLighting() && lightmapIndirectImage) { + GenerateLightmapIndirectFunc(fragmentShader, &lightmapIndirectImage->m_Image); + } + if (Material().HasLighting() && lightmapRadisoityImage) { + GenerateLightmapRadiosityFunc(fragmentShader, &lightmapRadisoityImage->m_Image); + } + if (Material().HasLighting() && lightmapShadowImage) { + GenerateLightmapShadowFunc(fragmentShader, &lightmapShadowImage->m_Image); + } + + if (Material().HasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) + GenerateLightmapIndirectSetupCode(fragmentShader, lightmapIndirectImage, + lightmapRadisoityImage); + + if (Material().HasLighting()) { + ApplyEmissiveMask(fragmentShader, Material().m_EmissiveMap2); + } + + // setup main + VertexGenerator().BeginFragmentGeneration(); + + // since we do pixel lighting we always need this if lighting is enabled + // We write this here because the functions below may also write to + // the fragment shader + if (Material().HasLighting()) { + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateVarTangentAndBinormal(); + vertexShader.GenerateWorldPosition(); + + if (Material().IsSpecularEnabled()) + vertexShader.GenerateViewVector(); + } + + fragmentShader << " initializeBaseFragmentVariables();" << Endl; + fragmentShader << " computeTemporaries();" << Endl; + fragmentShader << " normal = normalize( computeNormal() );" << Endl; + fragmentShader << " initializeLayerVariables();" << Endl; + fragmentShader << " float alpha = clamp( evalCutout(), 0.0, 1.0 );" << Endl; + + if (Material().IsCutOutEnabled()) { + fragmentShader << " if ( alpha <= 0.0f )" << Endl; + fragmentShader << " discard;" << Endl; + } + + // indirect / direct lightmap init + if (Material().HasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) + fragmentShader << " initializeLayerVariablesWithLightmap();" << Endl; + + // shadow map + GenerateLightmapShadowCode(fragmentShader, lightmapShadowImage); + + // main Body + fragmentShader << "#include \"customMaterialFragBodyAO.glsllib\"" << Endl; + + // for us right now transparency means we render a glass style material + if (m_HasTransparency && !Material().IsTransmissive()) + fragmentShader << " rgba = computeGlass( normal, materialIOR, alpha, rgba );" << Endl; + if (Material().IsTransmissive()) + fragmentShader << " rgba = computeOpacity( rgba );" << Endl; + + if (VertexGenerator().HasActiveWireframe()) { + fragmentShader.Append("vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;"); + fragmentShader.Append( + "\tfloat d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);"); + fragmentShader.Append("\tfloat mixVal = smoothstep(0.0, 1.0, d);"); // line width 1.0 + + fragmentShader.Append("\trgba = mix( vec4(0.0, 1.0, 0.0, 1.0), rgba, mixVal);"); + } + fragmentShader << " rgba.a *= object_opacity;" << Endl; + if (m_RenderContext.GetRenderContext().GetRenderContextType() + == NVRenderContextValues::GLES2) + fragmentShader << " gl_FragColor = rgba;" << Endl; + else + fragmentShader << " fragColor = rgba;" << Endl; + return false; + } + + NVRenderShaderProgram *GenerateCustomMaterialShader(const char8_t *inShaderPrefix, + const char8_t *inCustomMaterialName) + { + // build a string that allows us to print out the shader we are generating to the log. + // This is time consuming but I feel like it doesn't happen all that often and is very + // useful to users + // looking at the log file. + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(nonNull(inShaderPrefix)); + m_GeneratedShaderString.append(inCustomMaterialName); + SShaderDefaultMaterialKey theKey(Key()); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + + bool hasCustomVertexShader = GenerateVertexShader(theKey, inCustomMaterialName); + bool hasCustomFragmentShader = GenerateFragmentShader(theKey, inCustomMaterialName, + hasCustomVertexShader); + + VertexGenerator().EndVertexGeneration(hasCustomVertexShader); + VertexGenerator().EndFragmentGeneration(hasCustomFragmentShader); + + NVRenderShaderProgram *program = ProgramGenerator().CompileGeneratedShader( + m_GeneratedShaderString.c_str(), SShaderCacheProgramFlags(), FeatureSet()); + if (program && hasCustomVertexShader) { + // Change uniforms names to match runtime 2.x uniforms + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(*program)); + shader.m_ModelMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("modelMatrix", + *program); + shader.m_ViewProjMatrix = NVRenderCachedShaderProperty<QT3DSMat44>( + "modelViewProjection", *program); + shader.m_ViewMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewMatrix", *program); + shader.m_NormalMatrix = NVRenderCachedShaderProperty<QT3DSMat33>("modelNormalMatrix", + *program); + shader.m_ProjMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewProjectionMatrix", + *program); + shader.m_ViewportMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewportMatrix", + *program); + shader.m_CameraPos = NVRenderCachedShaderProperty<QT3DSVec3>("eyePosition", *program); + } + return program; + } + + virtual NVRenderShaderProgram * + GenerateShader(const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, + bool inHasTransparency, const char8_t *inShaderPrefix, + const char8_t *inCustomMaterialName) override + { + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::CustomMaterial); + m_CurrentMaterial = reinterpret_cast<const SCustomMaterial *>(&inMaterial); + m_CurrentKey = &inShaderDescription; + m_CurrentPipeline = static_cast<IDefaultMaterialVertexPipeline *>(&inVertexPipeline); + m_CurrentFeatureSet = inFeatureSet; + m_Lights = inLights; + m_FirstImage = inFirstImage; + m_HasTransparency = inHasTransparency; + + return GenerateCustomMaterialShader(inShaderPrefix, inCustomMaterialName); + } +}; +} + +ICustomMaterialShaderGenerator & +ICustomMaterialShaderGenerator::CreateCustomMaterialShaderGenerator(IQt3DSRenderContext &inRc) +{ + return *QT3DS_NEW(inRc.GetAllocator(), SShaderGenerator)(inRc); +} diff --git a/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.h b/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.h new file mode 100644 index 0000000..44e2c06 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CUSTOM_MATERIAL_SHADER_GENERATOR_H +#define QT3DS_RENDER_CUSTOM_MATERIAL_SHADER_GENERATOR_H +#include "Qt3DSRenderMaterialShaderGenerator.h" + +namespace qt3ds { +namespace render { + + class Qt3DSShadowMap; + + class ICustomMaterialShaderGenerator : public IMaterialShaderGenerator + { + public: + SImageVariableNames GetImageVariableNames(QT3DSU32 inIdx) override = 0; + void GenerateImageUVCoordinates(IShaderStageGenerator &inVertexPipeline, QT3DSU32 idx, + QT3DSU32 uvSet, SRenderableImage &image) override = 0; + + // inPipelineName needs to be unique else the shader cache will just return shaders from + // different pipelines. + NVRenderShaderProgram *GenerateShader( + const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, bool inHasTransparency, + const char8_t *inVertexPipelineName, const char8_t *inCustomMaterialName = "") override = 0; + + // Also sets the blend function on the render context. + virtual void + SetMaterialProperties(NVRenderShaderProgram &inProgram, const SGraphObject &inMaterial, + const QT3DSVec2 &inCameraVec, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override = 0; + + static ICustomMaterialShaderGenerator & + CreateCustomMaterialShaderGenerator(IQt3DSRenderContext &inRenderContext); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp b/src/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp new file mode 100644 index 0000000..aaf799f --- /dev/null +++ b/src/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp @@ -0,0 +1,2131 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderCustomMaterialRenderContext.h" +#include "Qt3DSRenderContextCore.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderDynamicObjectSystemCommands.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderResourceManager.h" +#include "Qt3DSRenderMesh.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderLayer.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "render/Qt3DSRenderComputeShader.h" +#include "foundation/PreAllocatedAllocator.h" +#include "foundation/SerializationTypes.h" +#include "foundation/Qt3DSTime.h" +#include "Qt3DSRenderDynamicObjectSystemUtil.h" +#include "Qt3DSRenderableImage.h" +#include "rendererimpl/Qt3DSVertexPipelineImpl.h" +#include "rendererimpl/Qt3DSRendererImplLayerRenderData.h" +#include "Qt3DSRenderCustomMaterialShaderGenerator.h" +#include "Qt3DSRenderModel.h" +#include "Qt3DSOffscreenRenderKey.h" + +using namespace qt3ds::render; +using namespace qt3ds::render::dynamic; +using qt3ds::render::NVRenderContextScopedProperty; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +SCustomMaterialVertexPipeline::SCustomMaterialVertexPipeline(IQt3DSRenderContext *inContext, + TessModeValues::Enum inTessMode) + : SVertexPipelineImpl( + inContext->GetAllocator(), inContext->GetCustomMaterialShaderGenerator(), + inContext->GetShaderProgramGenerator(), inContext->GetStringTable(), false) + , m_Context(inContext) + , m_TessMode(TessModeValues::NoTess) +{ + if (m_Context->GetRenderContext().IsTessellationSupported()) { + m_TessMode = inTessMode; + } + + if (m_Context->GetRenderContext().IsGeometryStageSupported() + && m_TessMode != TessModeValues::NoTess) { + m_Wireframe = inContext->GetWireframeMode(); + } +} + +void SCustomMaterialVertexPipeline::InitializeTessControlShader() +{ + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl) == NULL) { + return; + } + + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + + SetupTessIncludes(ShaderGeneratorStages::TessControl, m_TessMode); + + tessCtrlShader.Append("void main() {\n"); + + tessCtrlShader.Append("\tctWorldPos[0] = varWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = varWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = varWorldPos[2];"); + + if (m_TessMode == TessModeValues::TessPhong || m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctNorm[0] = varObjectNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = varObjectNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = varObjectNormal[2];"); + } + if (m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctTangent[0] = varObjTangent[0];"); + tessCtrlShader.Append("\tctTangent[1] = varObjTangent[1];"); + tessCtrlShader.Append("\tctTangent[2] = varObjTangent[2];"); + } + + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); +} + +void SCustomMaterialVertexPipeline::InitializeTessEvaluationShader() +{ + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval) == NULL) { + return; + } + + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("normal_matrix", "mat3"); + + SetupTessIncludes(ShaderGeneratorStages::TessEval, m_TessMode); + + if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) { + tessEvalShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddUniform("displace_tiling", "vec3"); + tessEvalShader.AddUniform("displaceAmount", "float"); + tessEvalShader.AddUniform(m_DisplacementImage->m_Image.m_ImageShaderName.c_str(), + "sampler2D"); + } + + tessEvalShader.Append("void main() {"); + + if (m_TessMode == TessModeValues::TessNPatch) { + tessEvalShader.Append("\tctNorm[0] = varObjectNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = varObjectNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = varObjectNormalTC[2];"); + + tessEvalShader.Append("\tctTangent[0] = varTangentTC[0];"); + tessEvalShader.Append("\tctTangent[1] = varTangentTC[1];"); + tessEvalShader.Append("\tctTangent[2] = varTangentTC[2];"); + } + + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); +} + +void SCustomMaterialVertexPipeline::FinalizeTessControlShader() +{ + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessCtrlShader << "\t" << iter->first.c_str() + << "TC[gl_InvocationID] = " << iter->first.c_str() + << "[gl_InvocationID];\n"; + } +} + +void SCustomMaterialVertexPipeline::FinalizeTessEvaluationShader() +{ + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + eastl::string outExt(""); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + outExt = "TE"; + + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + if (m_TessMode == TessModeValues::TessNPatch) { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.z * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.x * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[2];\n"; + } + + // transform the normal + if (m_GenerationFlags & GenerationFlagValues::WorldNormal) + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * teNorm);\n"; + // transform the tangent + if (m_GenerationFlags & GenerationFlagValues::TangentBinormal) { + tessEvalShader << "\n\tvarTangent" << outExt.c_str() + << " = normalize(normal_matrix * teTangent);\n"; + // transform the binormal + tessEvalShader << "\n\tvarBinormal" << outExt.c_str() + << " = normalize(normal_matrix * teBinormal);\n"; + } + } else { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.x * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.z * " << iter->first.c_str() << "TC[2];\n"; + } + + // displacement mapping makes only sense with linear tessellation + if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) { + tessEvalShader + << "\ttexture_coordinate_info tmp = textureCoordinateInfo( varTexCoord0" + << outExt.c_str() << ", varTangent" << outExt.c_str() << ", varBinormal" + << outExt.c_str() << " );" << Endl; + tessEvalShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( " + "0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, " + "0.000000 ), displace_tiling ), tmp);" + << Endl; + + tessEvalShader << "\tpos.xyz = defaultMaterialFileDisplacementTexture( " + << m_DisplacementImage->m_Image.m_ImageShaderName.c_str() + << ", displaceAmount, " + << "tmp.position.xy"; + tessEvalShader << ", varObjectNormal" << outExt.c_str() << ", pos.xyz );" << Endl; + tessEvalShader << "\tvarWorldPos" << outExt.c_str() << "= (model_matrix * pos).xyz;" + << Endl; + } + + // transform the normal + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * varObjectNormal" << outExt.c_str() + << ");\n"; + } + + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); +} + +// Responsible for beginning all vertex and fragment generation (void main() { etc). +void SCustomMaterialVertexPipeline::BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) +{ + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + + if (m_TessMode != TessModeValues::NoTess) { + theStages |= ShaderGeneratorStages::TessControl; + theStages |= ShaderGeneratorStages::TessEval; + } + if (m_Wireframe) { + theStages |= ShaderGeneratorStages::Geometry; + } + + ProgramGenerator().BeginProgram(theStages); + + if (m_TessMode != TessModeValues::NoTess) { + InitializeTessControlShader(); + InitializeTessEvaluationShader(); + } + if (m_Wireframe) { + InitializeWireframeGeometryShader(); + } + + IShaderStageGenerator &vertexShader(Vertex()); + + // thinks we need + vertexShader.AddInclude("viewProperties.glsllib"); + vertexShader.AddInclude("customMaterial.glsllib"); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader << "void main()" << Endl << "{" << Endl; + + if (displacementImage) { + GenerateUVCoords(0); + if (!HasTessellation()) { + vertexShader.AddUniform("displaceAmount", "float"); + vertexShader.AddUniform("displace_tiling", "vec3"); + // we create the world position setup here + // because it will be replaced with the displaced position + SetCode(GenerationFlagValues::WorldPosition); + vertexShader.AddUniform("model_matrix", "mat4"); + + vertexShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + vertexShader.AddUniform(displacementImage->m_Image.m_ImageShaderName.c_str(), + "sampler2D"); + + vertexShader << "\ttexture_coordinate_info tmp = textureCoordinateInfo( texCoord0, " + "varTangent, varBinormal );" + << Endl; + vertexShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( " + "0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, " + "0.000000 ), displace_tiling ), tmp);" + << Endl; + + vertexShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + << displacementImage->m_Image.m_ImageShaderName.c_str() + << ", displaceAmount, " + << "tmp.position.xy" + << ", attr_norm, attr_pos );" << Endl; + + AddInterpolationParameter("varWorldPos", "vec3"); + vertexShader.Append("\tvec3 local_model_world_position = (model_matrix * " + "vec4(displacedPos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + } + + if (HasTessellation()) { + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + } else { + vertexShader.AddUniform("model_view_projection", "mat4"); + if (displacementImage) + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + else + vertexShader.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + } + + if (HasTessellation()) { + GenerateWorldPosition(); + GenerateWorldNormal(); + GenerateObjectNormal(); + GenerateVarTangentAndBinormal(); + } +} + +void SCustomMaterialVertexPipeline::BeginFragmentGeneration() +{ + Fragment().AddUniform("object_opacity", "float"); + Fragment() << "void main()" << Endl << "{" << Endl; +} + +void SCustomMaterialVertexPipeline::AssignOutput(const char8_t *inVarName, + const char8_t *inVarValue) +{ + Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n"; +} + +void SCustomMaterialVertexPipeline::GenerateUVCoords(QT3DSU32 inUVSet) +{ + if (inUVSet == 0 && SetCode(GenerationFlagValues::UVCoords)) + return; + if (inUVSet == 1 && SetCode(GenerationFlagValues::UVCoords1)) + return; + + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) + AddInterpolationParameter("varTexCoord0", "vec3"); + else if (inUVSet == 1) + AddInterpolationParameter("varTexCoord1", "vec3"); + + DoGenerateUVCoords(inUVSet); +} + +void SCustomMaterialVertexPipeline::GenerateWorldNormal() +{ + if (SetCode(GenerationFlagValues::WorldNormal)) + return; + AddInterpolationParameter("varNormal", "vec3"); + DoGenerateWorldNormal(); +} + +void SCustomMaterialVertexPipeline::GenerateObjectNormal() +{ + if (SetCode(GenerationFlagValues::ObjectNormal)) + return; + DoGenerateObjectNormal(); +} + +void SCustomMaterialVertexPipeline::GenerateVarTangentAndBinormal() +{ + if (SetCode(GenerationFlagValues::TangentBinormal)) + return; + AddInterpolationParameter("varTangent", "vec3"); + AddInterpolationParameter("varBinormal", "vec3"); + AddInterpolationParameter("varObjTangent", "vec3"); + AddInterpolationParameter("varObjBinormal", "vec3"); + DoGenerateVarTangentAndBinormal(); +} + +void SCustomMaterialVertexPipeline::GenerateWorldPosition() +{ + if (SetCode(GenerationFlagValues::WorldPosition)) + return; + + ActiveStage().AddUniform("model_matrix", "mat4"); + AddInterpolationParameter("varWorldPos", "vec3"); + AddInterpolationParameter("varObjPos", "vec3"); + DoGenerateWorldPosition(); +} + +// responsible for closing all vertex and fragment generation +void SCustomMaterialVertexPipeline::EndVertexGeneration(bool customShader) +{ + if (HasTessellation()) { + // finalize tess control shader + FinalizeTessControlShader(); + // finalize tess evaluation shader + FinalizeTessEvaluationShader(); + + TessControl().Append("}"); + TessEval().Append("}"); + + if (m_Wireframe) { + // finalize geometry shader + FinalizeWireframeGeometryShader(); + Geometry().Append("}"); + } + } + + if (!customShader) + Vertex().Append("}"); +} + +void SCustomMaterialVertexPipeline::EndFragmentGeneration(bool customShader) +{ + if (!customShader) + Fragment().Append("}"); +} + +IShaderStageGenerator &SCustomMaterialVertexPipeline::ActiveStage() +{ + return Vertex(); +} + +void SCustomMaterialVertexPipeline::AddInterpolationParameter(const char8_t *inName, + const char8_t *inType) +{ + m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType))); + Vertex().AddOutgoing(inName, inType); + Fragment().AddIncoming(inName, inType); + + if (HasTessellation()) { + eastl::string nameBuilder(inName); + nameBuilder.append("TC"); + TessControl().AddOutgoing(nameBuilder.c_str(), inType); + + nameBuilder.assign(inName); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) { + nameBuilder.append("TE"); + Geometry().AddOutgoing(inName, inType); + } + TessEval().AddOutgoing(nameBuilder.c_str(), inType); + } +} + +void SCustomMaterialVertexPipeline::DoGenerateUVCoords(QT3DSU32 inUVSet) +{ + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) { + Vertex().AddIncoming("attr_uv0", "vec2"); + Vertex() << "\tvec3 texCoord0 = vec3( attr_uv0, 0.0 );" << Endl; + AssignOutput("varTexCoord0", "texCoord0"); + } else if (inUVSet == 1) { + Vertex().AddIncoming("attr_uv1", "vec2"); + Vertex() << "\tvec3 texCoord1 = vec3( attr_uv1, 1.0 );" << Endl; + AssignOutput("varTexCoord1", "texCoord1"); + } +} + +void SCustomMaterialVertexPipeline::DoGenerateWorldNormal() +{ + IShaderStageGenerator &vertexGenerator(Vertex()); + vertexGenerator.AddIncoming("attr_norm", "vec3"); + vertexGenerator.AddUniform("normal_matrix", "mat3"); + + if (HasTessellation() == false) { + Vertex().Append("\tvarNormal = normalize( normal_matrix * attr_norm );"); + } +} + +void SCustomMaterialVertexPipeline::DoGenerateObjectNormal() +{ + AddInterpolationParameter("varObjectNormal", "vec3"); + Vertex().Append("\tvarObjectNormal = attr_norm;"); +} + +void SCustomMaterialVertexPipeline::DoGenerateWorldPosition() +{ + Vertex().Append("\tvarObjPos = attr_pos;"); + Vertex().Append("\tvec4 worldPos = (model_matrix * vec4(attr_pos, 1.0));"); + AssignOutput("varWorldPos", "worldPos.xyz"); +} + +void SCustomMaterialVertexPipeline::DoGenerateVarTangentAndBinormal() +{ + Vertex().AddIncoming("attr_textan", "vec3"); + Vertex().AddIncoming("attr_binormal", "vec3"); + + Vertex() << "\tvarTangent = normal_matrix * attr_textan;" << Endl + << "\tvarBinormal = normal_matrix * attr_binormal;" << Endl; + + Vertex() << "\tvarObjTangent = attr_textan;" << Endl << "\tvarObjBinormal = attr_binormal;" + << Endl; +} + +void SCustomMaterialVertexPipeline::DoGenerateVertexColor() +{ + Vertex().AddIncoming("attr_color", "vec3"); + Vertex().Append("\tvarColor = attr_color;"); +} + + +struct SMaterialClass +{ + NVAllocatorCallback *m_Allocator; + IDynamicObjectClass *m_Class; + bool m_HasTransparency; + bool m_HasRefraction; + bool m_HasDisplacement; + bool m_AlwaysDirty; + QT3DSU32 m_ShaderKey; + QT3DSU32 m_LayerCount; + QT3DSI32 mRefCount; + SMaterialClass(NVAllocatorCallback &alloc, IDynamicObjectClass &inCls) + : m_Allocator(&alloc) + , m_Class(&inCls) + , m_HasTransparency(false) + , m_HasRefraction(false) + , m_HasDisplacement(false) + , m_AlwaysDirty(false) + , m_ShaderKey(0) + , m_LayerCount(0) + , mRefCount(0) + { + } + + ~SMaterialClass() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(*m_Allocator) + + void AfterWrite() + { + m_Allocator = NULL; + m_Class = NULL; + mRefCount = 0; + } + + void AfterRead(NVAllocatorCallback &alloc, IDynamicObjectClass &inCls) + { + m_Allocator = &alloc; + m_Class = &inCls; + mRefCount = 0; + } +}; + +typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SMaterialClass>> TStringMaterialMap; +typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair; + +namespace eastl { +template <> +struct hash<TStrStrPair> +{ + size_t operator()(const TStrStrPair &item) const + { + return hash<CRegisteredString>()(item.first) ^ hash<CRegisteredString>()(item.second); + } +}; +} + +struct SShaderMapKey +{ + TStrStrPair m_Name; + eastl::vector<SShaderPreprocessorFeature> m_Features; + TessModeValues::Enum m_TessMode; + bool m_WireframeMode; + SShaderDefaultMaterialKey m_MaterialKey; + size_t m_HashCode; + SShaderMapKey(TStrStrPair inName, TShaderFeatureSet inFeatures, TessModeValues::Enum inTessMode, + bool inWireframeMode, SShaderDefaultMaterialKey inMaterialKey) + : m_Name(inName) + , m_Features(inFeatures.begin(), inFeatures.end()) + , m_TessMode(inTessMode) + , m_WireframeMode(inWireframeMode) + , m_MaterialKey(inMaterialKey) + { + m_HashCode = eastl::hash<TStrStrPair>()(m_Name) + ^ HashShaderFeatureSet(toDataRef(m_Features.data(), (QT3DSU32)m_Features.size())) + ^ eastl::hash<QT3DSU32>()(m_TessMode) ^ eastl::hash<bool>()(m_WireframeMode) + ^ eastl::hash<size_t>()(inMaterialKey.hash()); + } + bool operator==(const SShaderMapKey &inKey) const + { + return m_Name == inKey.m_Name && m_Features == inKey.m_Features + && m_TessMode == inKey.m_TessMode && m_WireframeMode == inKey.m_WireframeMode + && m_MaterialKey == inKey.m_MaterialKey; + } +}; + +namespace eastl { +template <> +struct hash<SShaderMapKey> +{ + size_t operator()(const SShaderMapKey &inKey) const { return inKey.m_HashCode; } +}; +} + +namespace { + +struct SCustomMaterialTextureData +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + qt3ds::render::NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + qt3ds::render::NVRenderTexture2D *m_Texture; + bool m_needsMips; + volatile QT3DSI32 mRefCount; + + SCustomMaterialTextureData(NVRenderShaderProgram &inShader, NVRenderTexture2D *inTexture, + const char *inTexName, bool needMips) + : m_Shader(inShader) + , m_Sampler(inTexName, inShader) + , m_Texture(inTexture) + , m_needsMips(needMips) + , mRefCount(0) + { + } + + void Set(const SPropertyDefinition *inDefinition) + { + if (m_Texture && inDefinition) { + m_Texture->SetMagFilter(inDefinition->m_MagFilterOp); + m_Texture->SetMinFilter(inDefinition->m_MinFilterOp); + m_Texture->SetTextureWrapS(inDefinition->m_CoordOp); + m_Texture->SetTextureWrapT(inDefinition->m_CoordOp); + } else if (m_Texture) { + // set some defaults + m_Texture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + m_Texture->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + m_Texture->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + } + + if ((m_Texture->GetNumMipmaps() == 0) && m_needsMips) + m_Texture->GenerateMipmaps(); + + m_Sampler.Set(m_Texture); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + static SCustomMaterialTextureData CreateTextureEntry(NVRenderShaderProgram &inShader, + NVRenderTexture2D *inTexture, + const char *inTexName, bool needMips) + { + return SCustomMaterialTextureData(inShader, inTexture, inTexName, needMips); + } +}; + +typedef eastl::pair<CRegisteredString, NVScopedRefCounted<SCustomMaterialTextureData>> + TCustomMaterialTextureEntry; + +/** + * Cached tessellation property lookups this is on a per mesh base + */ +struct SCustomMaterialsTessellationProperties +{ + NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessLevel; ///< tesselation value for the edges + NVRenderCachedShaderProperty<QT3DSF32> m_InsideTessLevel; ///< tesselation value for the inside + NVRenderCachedShaderProperty<QT3DSF32> + m_PhongBlend; ///< blending between linear and phong component + NVRenderCachedShaderProperty<QT3DSVec2> + m_DistanceRange; ///< distance range for min and max tess level + NVRenderCachedShaderProperty<QT3DSF32> m_DisableCulling; ///< if set to 1.0 this disables backface + ///culling optimization in the tess shader + + SCustomMaterialsTessellationProperties() {} + SCustomMaterialsTessellationProperties(NVRenderShaderProgram &inShader) + : m_EdgeTessLevel("tessLevelOuter", inShader) + , m_InsideTessLevel("tessLevelInner", inShader) + , m_PhongBlend("phongBlend", inShader) + , m_DistanceRange("distanceRange", inShader) + , m_DisableCulling("disableCulling", inShader) + { + } +}; + +/* We setup some shared state on the custom material shaders */ +struct SCustomMaterialShader +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_ModelMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewProjMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSMat33> m_NormalMatrix; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPos; + NVRenderCachedShaderProperty<QT3DSMat44> m_ProjMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewportMatrix; + NVRenderCachedShaderProperty<QT3DSVec2> m_CamProperties; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AOTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeProps; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOpts; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeRot; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOfs; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe2; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbe2Props; + NVRenderCachedShaderProperty<QT3DSI32> m_LightCount; + NVRenderCachedShaderProperty<QT3DSI32> m_AreaLightCount; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + SCustomMaterialsTessellationProperties m_Tessellation; + SDynamicShaderProgramFlags m_ProgramFlags; + volatile QT3DSI32 mRefCount; + SCustomMaterialShader(NVRenderShaderProgram &inShader, SDynamicShaderProgramFlags inFlags) + : m_Shader(inShader) + , m_ModelMatrix("model_matrix", inShader) + , m_ViewProjMatrix("model_view_projection", inShader) + , m_ViewMatrix("view_matrix", inShader) + , m_NormalMatrix("normal_matrix", inShader) + , m_CameraPos("camera_position", inShader) + , m_ProjMatrix("view_projection_matrix", inShader) + , m_ViewportMatrix("viewport_matrix", inShader) + , m_CamProperties("camera_properties", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_AOTexture("ao_sampler", inShader) + , m_LightProbe("light_probe", inShader) + , m_LightProbeProps("light_probe_props", inShader) + , m_LightProbeOpts("light_probe_opts", inShader) + , m_LightProbeRot("light_probe_rotation", inShader) + , m_LightProbeOfs("light_probe_offset", inShader) + , m_LightProbe2("light_probe2", inShader) + , m_LightProbe2Props("light_probe2_props", inShader) + , m_LightCount("uNumLights", inShader) + , m_AreaLightCount("uNumAreaLights", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_Tessellation(inShader) + , m_ProgramFlags(inFlags) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) +}; + +struct SMaterialOrComputeShader +{ + SCustomMaterialShader *m_MaterialShader; + NVRenderShaderProgram *m_ComputeShader; + SMaterialOrComputeShader() + : m_MaterialShader(NULL) + , m_ComputeShader(NULL) + { + } + SMaterialOrComputeShader(SCustomMaterialShader &inMaterialShader) + : m_MaterialShader(&inMaterialShader) + , m_ComputeShader(NULL) + { + } + SMaterialOrComputeShader(NVRenderShaderProgram &inComputeShader) + : m_MaterialShader(NULL) + , m_ComputeShader(&inComputeShader) + { + QT3DS_ASSERT(inComputeShader.GetProgramType() == NVRenderShaderProgram::ProgramType::Compute); + } + bool IsValid() const { return m_MaterialShader || m_ComputeShader; } + bool IsComputeShader() const { return m_ComputeShader != NULL; } + bool IsMaterialShader() const { return m_MaterialShader != NULL; } + SCustomMaterialShader &MaterialShader() + { + QT3DS_ASSERT(IsMaterialShader()); + return *m_MaterialShader; + } + NVRenderShaderProgram &ComputeShader() + { + QT3DS_ASSERT(IsComputeShader()); + return *m_ComputeShader; + } +}; + +struct SCustomMaterialBuffer +{ + CRegisteredString m_Name; + NVScopedRefCounted<NVRenderFrameBuffer> m_FrameBuffer; + NVScopedRefCounted<NVRenderTexture2D> m_Texture; + SAllocateBufferFlags m_Flags; + + SCustomMaterialBuffer(CRegisteredString inName, NVRenderFrameBuffer &inFb, + NVRenderTexture2D &inTexture, SAllocateBufferFlags inFlags) + : m_Name(inName) + , m_FrameBuffer(&inFb) + , m_Texture(&inTexture) + , m_Flags(inFlags) + { + } + SCustomMaterialBuffer() {} +}; + +struct SMaterialSystem; +typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderVertexBuffer>> + TStringVertexBufferMap; +typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderInputAssembler>> + TStringAssemblerMap; + +struct SStringMemoryBarrierFlagMap +{ + const char8_t *m_Name; + qt3ds::render::NVRenderBufferBarrierValues::Enum m_Value; + SStringMemoryBarrierFlagMap(const char8_t *nm, + qt3ds::render::NVRenderBufferBarrierValues::Enum val) + : m_Name(nm) + , m_Value(val) + { + } +}; + +SStringMemoryBarrierFlagMap g_StringMemoryFlagMap[] = { + SStringMemoryBarrierFlagMap("vertex_attribute", + qt3ds::render::NVRenderBufferBarrierValues::VertexAttribArray), + SStringMemoryBarrierFlagMap("element_array", + qt3ds::render::NVRenderBufferBarrierValues::ElementArray), + SStringMemoryBarrierFlagMap("uniform_buffer", + qt3ds::render::NVRenderBufferBarrierValues::UniformBuffer), + SStringMemoryBarrierFlagMap("texture_fetch", + qt3ds::render::NVRenderBufferBarrierValues::TextureFetch), + SStringMemoryBarrierFlagMap("shader_image_access", + qt3ds::render::NVRenderBufferBarrierValues::ShaderImageAccess), + SStringMemoryBarrierFlagMap("command_buffer", + qt3ds::render::NVRenderBufferBarrierValues::CommandBuffer), + SStringMemoryBarrierFlagMap("pixel_buffer", + qt3ds::render::NVRenderBufferBarrierValues::PixelBuffer), + SStringMemoryBarrierFlagMap("texture_update", + qt3ds::render::NVRenderBufferBarrierValues::TextureUpdate), + SStringMemoryBarrierFlagMap("buffer_update", + qt3ds::render::NVRenderBufferBarrierValues::BufferUpdate), + SStringMemoryBarrierFlagMap("frame_buffer", + qt3ds::render::NVRenderBufferBarrierValues::Framebuffer), + SStringMemoryBarrierFlagMap("transform_feedback", + qt3ds::render::NVRenderBufferBarrierValues::TransformFeedback), + SStringMemoryBarrierFlagMap("atomic_counter", + qt3ds::render::NVRenderBufferBarrierValues::AtomicCounter), + SStringMemoryBarrierFlagMap("shader_storage", + qt3ds::render::NVRenderBufferBarrierValues::ShaderStorage), +}; + +struct SStringBlendFuncMap +{ + const char8_t *m_Name; + qt3ds::render::NVRenderSrcBlendFunc::Enum m_Value; + SStringBlendFuncMap(const char8_t *nm, qt3ds::render::NVRenderSrcBlendFunc::Enum val) + : m_Name(nm) + , m_Value(val) + { + } +}; + +SStringBlendFuncMap g_BlendFuncMap[] = { +#define QT3DS_RENDER_HANDLE_BLEND_FUNC(nm) \ + SStringBlendFuncMap(#nm, qt3ds::render::NVRenderSrcBlendFunc::nm), +#define QT3DS_RENDER_HANDLE_SRC_BLEND_FUNC(nm) \ + SStringBlendFuncMap(#nm, qt3ds::render::NVRenderSrcBlendFunc::nm), + QT3DS_RENDER_ITERATE_BLEND_FUNC +#undef QT3DS_RENDER_HANDLE_BLEND_FUNC +#undef QT3DS_RENDER_HANDLE_SRC_BLEND_FUNC +}; + +struct SMaterialSystem : public ICustomMaterialSystem +{ + typedef nvhash_map<SShaderMapKey, NVScopedRefCounted<SCustomMaterialShader>> TShaderMap; + typedef eastl::pair<CRegisteredString, SImage *> TAllocatedImageEntry; + + IQt3DSRenderContextCore &m_CoreContext; + IQt3DSRenderContext *m_Context; + mutable qt3ds::render::SPreAllocatedAllocator m_Allocator; + TStringMaterialMap m_StringMaterialMap; + TShaderMap m_ShaderMap; + nvvector<TCustomMaterialTextureEntry> m_TextureEntries; + nvvector<SCustomMaterialBuffer> m_AllocatedBuffers; + nvvector<TAllocatedImageEntry> m_AllocatedImages; + bool m_UseFastBlits; + eastl::string m_ShaderNameBuilder; + QT3DSU64 m_LastFrameTime; + QT3DSF32 m_MillisecondsSinceLastFrame; + QT3DSI32 mRefCount; + + SMaterialSystem(IQt3DSRenderContextCore &ct) + : m_CoreContext(ct) + , m_Context(NULL) + , m_Allocator(ct.GetAllocator()) + , m_StringMaterialMap(ct.GetAllocator(), "SMaterialSystem::m_StringMaterialMap") + , m_ShaderMap(ct.GetAllocator(), "SMaterialSystem::m_ShaderMap") + , m_TextureEntries(ct.GetAllocator(), "SMaterialSystem::m_TextureEntries") + , m_AllocatedBuffers(ct.GetAllocator(), "SMaterialSystem::m_AllocatedBuffers") + , m_AllocatedImages(ct.GetAllocator(), "SMaterialSystem::m_AllocatedImages") + , m_UseFastBlits(true) + , m_LastFrameTime(0) + , m_MillisecondsSinceLastFrame(0) + , mRefCount(0) + { + } + + ~SMaterialSystem() + { + while (m_AllocatedBuffers.size()) + m_AllocatedBuffers.replace_with_last(0); + + for (QT3DSU32 idx = 0; idx < m_AllocatedImages.size(); ++idx) { + SImage *pImage = m_AllocatedImages[idx].second; + QT3DS_FREE(m_CoreContext.GetAllocator(), pImage); + } + m_AllocatedImages.clear(); + } + + void ReleaseBuffer(QT3DSU32 inIdx) + { + // Don't call this on MaterialSystem destroy. + // This causes issues for scene liftime buffers + // because the resource manager is destroyed before + IResourceManager &theManager(m_Context->GetResourceManager()); + SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[inIdx]); + theEntry.m_FrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer()); + + theManager.Release(*theEntry.m_FrameBuffer); + theManager.Release(*theEntry.m_Texture); + m_AllocatedBuffers.replace_with_last(inIdx); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_CoreContext.GetAllocator()) + + bool IsMaterialRegistered(CRegisteredString inStr) override + { + return m_StringMaterialMap.find(inStr) != m_StringMaterialMap.end(); + } + + bool RegisterMaterialClass(CRegisteredString inName, + NVConstDataRef<dynamic::SPropertyDeclaration> inProperties) override + { + if (IsMaterialRegistered(inName)) + return false; + m_CoreContext.GetDynamicObjectSystemCore().Register( + inName, inProperties, sizeof(SCustomMaterial), GraphObjectTypes::CustomMaterial); + IDynamicObjectClass *theClass = + m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return false; + } + SMaterialClass *theNewClass = QT3DS_NEW(m_Allocator, SMaterialClass)(m_Allocator, *theClass); + m_StringMaterialMap.insert(eastl::make_pair(inName, theNewClass)); + return true; + } + + SMaterialClass *GetMaterialClass(CRegisteredString inStr) + { + TStringMaterialMap::iterator theIter = m_StringMaterialMap.find(inStr); + if (theIter != m_StringMaterialMap.end()) + return theIter->second; + return NULL; + } + + const SMaterialClass *GetMaterialClass(CRegisteredString inStr) const + { + return const_cast<SMaterialSystem *>(this)->GetMaterialClass(inStr); + } + + virtual NVConstDataRef<SPropertyDefinition> + GetCustomMaterialProperties(CRegisteredString inCustomMaterialName) const override + { + IDynamicObjectClass *theMaterialClass = + m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inCustomMaterialName); + + if (theMaterialClass) + return theMaterialClass->GetProperties(); + + return NVConstDataRef<SPropertyDefinition>(); + } + + virtual QT3DSU32 FindBuffer(CRegisteredString inName) + { + for (QT3DSU32 idx = 0, end = m_AllocatedBuffers.size(); idx < end; ++idx) + if (m_AllocatedBuffers[idx].m_Name == inName) + return idx; + return m_AllocatedBuffers.size(); + } + + virtual QT3DSU32 FindAllocatedImage(CRegisteredString inName) + { + for (QT3DSU32 idx = 0, end = m_AllocatedImages.size(); idx < end; ++idx) + if (m_AllocatedImages[idx].first == inName) + return idx; + return QT3DSU32(-1); + } + + virtual bool TextureNeedsMips(const SPropertyDefinition *inPropDec, + qt3ds::render::NVRenderTexture2D *inTexture) + { + if (inPropDec && inTexture) { + return bool((inPropDec->m_MinFilterOp == NVRenderTextureMinifyingOp::LinearMipmapLinear) + && (inTexture->GetNumMipmaps() == 0)); + } + + return false; + } + + virtual void SetTexture(NVRenderShaderProgram &inShader, CRegisteredString inPropName, + NVRenderTexture2D *inTexture, + const SPropertyDefinition *inPropDec = NULL, bool needMips = false) + { + SCustomMaterialTextureData *theTextureEntry(NULL); + for (QT3DSU32 idx = 0, end = m_TextureEntries.size(); idx < end && theTextureEntry == NULL; + ++idx) { + if (m_TextureEntries[idx].first == inPropName + && m_TextureEntries[idx].second->m_Shader.mPtr == &inShader + && m_TextureEntries[idx].second->m_Texture == inTexture) { + theTextureEntry = m_TextureEntries[idx].second; + break; + } + } + if (theTextureEntry == NULL) { + NVScopedRefCounted<SCustomMaterialTextureData> theNewEntry = + QT3DS_NEW(m_CoreContext.GetAllocator(), SCustomMaterialTextureData)( + SCustomMaterialTextureData::CreateTextureEntry(inShader, inTexture, inPropName, + needMips)); + m_TextureEntries.push_back(eastl::make_pair(inPropName, theNewEntry)); + theTextureEntry = theNewEntry.mPtr; + } + theTextureEntry->Set(inPropDec); + } + + void SetSubpresentation(NVRenderShaderProgram &inShader, CRegisteredString inPropName, + NVRenderTexture2D *inTexture, + const SPropertyDefinition *inPropDec) + { + SPropertyDefinition propDef = *inPropDec; + propDef.m_MinFilterOp = NVRenderTextureMinifyingOp::Linear; + propDef.m_MagFilterOp = NVRenderTextureMagnifyingOp::Linear; + SCustomMaterialTextureData::CreateTextureEntry(inShader, inTexture, inPropName, false) + .Set(&propDef); + } + + void SetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetPropertyEnumNames(inName, inPropName, + inNames); + } + + void SetPropertyTextureSettings(CRegisteredString inName, CRegisteredString inPropName, + CRegisteredString inPropPath, + NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, + NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + if (theClass && inTexType == NVRenderTextureTypeValue::Displace) { + theClass->m_HasDisplacement = true; + } + m_CoreContext.GetDynamicObjectSystemCore().SetPropertyTextureSettings( + inName, inPropName, inPropPath, inTexType, inCoordOp, inMagFilterOp, inMinFilterOp); + } + + void SetMaterialClassShader(CRegisteredString inName, const char8_t *inShaderType, + const char8_t *inShaderVersion, const char8_t *inShaderData, + bool inHasGeomShader, bool inIsComputeShader) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetShaderData(inName, inShaderData, inShaderType, + inShaderVersion, inHasGeomShader, + inIsComputeShader); + } + + SCustomMaterial *CreateCustomMaterial(CRegisteredString inName, + NVAllocatorCallback &inSceneGraphAllocator) override + { + SCustomMaterial *theMaterial = static_cast<SCustomMaterial *>( + m_CoreContext.GetDynamicObjectSystemCore().CreateInstance(inName, + inSceneGraphAllocator)); + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theMaterial) { + QT3DSU32 key = 0, count = 0; + if (theClass) { + key = theClass->m_ShaderKey; + count = theClass->m_LayerCount; + } + theMaterial->Initialize(key, count); + } + + return theMaterial; + } + + void SetCustomMaterialTransparency(CRegisteredString inName, bool inHasTransparency) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + + theClass->m_HasTransparency = inHasTransparency; + } + + void SetCustomMaterialRefraction(CRegisteredString inName, bool inHasRefraction) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + + theClass->m_HasRefraction = inHasRefraction; + } + + void SetCustomMaterialAlwaysDirty(CRegisteredString inName, bool inIsAlwaysDirty) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + + theClass->m_AlwaysDirty = inIsAlwaysDirty; + } + + void SetCustomMaterialShaderKey(CRegisteredString inName, QT3DSU32 inShaderKey) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + + theClass->m_ShaderKey = inShaderKey; + } + + void SetCustomMaterialLayerCount(CRegisteredString inName, QT3DSU32 inLayerCount) override + { + SMaterialClass *theClass = GetMaterialClass(inName); + + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + + theClass->m_LayerCount = inLayerCount; + } + + void SetCustomMaterialCommands(CRegisteredString inName, + NVConstDataRef<dynamic::SCommand *> inCommands) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetRenderCommands(inName, inCommands); + } + + CRegisteredString GetShaderCacheKey(Qt3DSString &inShaderKeyBuffer, const char8_t *inId, + const char8_t *inProgramMacro, + const dynamic::SDynamicShaderProgramFlags &inFlags) + { + inShaderKeyBuffer.assign(inId); + if (inProgramMacro && *inProgramMacro) { + inShaderKeyBuffer.append("#"); + inShaderKeyBuffer.append(inProgramMacro); + } + if (inFlags.IsTessellationEnabled()) { + inShaderKeyBuffer.append("#"); + inShaderKeyBuffer.append(TessModeValues::toString(inFlags.m_TessMode)); + } + if (inFlags.IsGeometryShaderEnabled() && inFlags.m_WireframeMode) { + inShaderKeyBuffer.append("#"); + inShaderKeyBuffer.append(inFlags.wireframeToString(inFlags.m_WireframeMode)); + } + + return m_CoreContext.GetStringTable().RegisterStr(inShaderKeyBuffer.c_str()); + } + + NVRenderShaderProgram *GetShader(SCustomMaterialRenderContext &inRenderContext, + const SCustomMaterial &inMaterial, + const SBindShader &inCommand, TShaderFeatureSet inFeatureSet, + const dynamic::SDynamicShaderProgramFlags &inFlags) + { + ICustomMaterialShaderGenerator &theMaterialGenerator( + m_Context->GetCustomMaterialShaderGenerator()); + + // generate key + Qt3DSString theShaderKeyBuffer; + CRegisteredString theKey = GetShaderCacheKey(theShaderKeyBuffer, inCommand.m_ShaderPath, + inCommand.m_ShaderDefine, inFlags); + + SCustomMaterialVertexPipeline thePipeline(m_Context, + inRenderContext.m_Model.m_TessellationMode); + + NVRenderShaderProgram *theProgram = theMaterialGenerator.GenerateShader( + inMaterial, inRenderContext.m_MaterialKey, thePipeline, inFeatureSet, + inRenderContext.m_Lights, inRenderContext.m_FirstImage, + (inMaterial.m_hasTransparency || inMaterial.m_hasRefraction), + "custom material pipeline-- ", inCommand.m_ShaderPath.c_str()); + + return theProgram; + } + + SMaterialOrComputeShader BindShader(SCustomMaterialRenderContext &inRenderContext, + const SCustomMaterial &inMaterial, + const SBindShader &inCommand, + TShaderFeatureSet inFeatureSet) + { + NVRenderShaderProgram *theProgram = NULL; + eastl::vector<SShaderPreprocessorFeature> features; + for (int i = 0; i < inFeatureSet.size(); ++i) + features.push_back(inFeatureSet.mData[i]); + features.push_back(SShaderPreprocessorFeature(m_Context->GetStringTable() + .RegisterStr("NO_FRAG_OUTPUT"), true)); + TShaderFeatureSet featureSet(features.data(), features.size()); + + SDynamicShaderProgramFlags theFlags(inRenderContext.m_Model.m_TessellationMode, + inRenderContext.m_Subset.m_WireframeMode); + theFlags.SetTessellationEnabled(inRenderContext.m_Model.m_TessellationMode + != TessModeValues::NoTess); + theFlags.SetGeometryShaderEnabled(inRenderContext.m_Subset.m_WireframeMode); + + SShaderMapKey skey = SShaderMapKey( + TStrStrPair(inCommand.m_ShaderPath, inCommand.m_ShaderDefine), featureSet, + theFlags.m_TessMode, theFlags.m_WireframeMode, inRenderContext.m_MaterialKey); + eastl::pair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert( + eastl::make_pair(skey, NVScopedRefCounted<SCustomMaterialShader>(NULL)))); + + if (theInsertResult.second) { + theProgram = GetShader(inRenderContext, inMaterial, inCommand, featureSet, theFlags); + + if (theProgram) { + theInsertResult.first->second = + QT3DS_NEW(m_Allocator, SCustomMaterialShader)(*theProgram, theFlags); + } + } else if (theInsertResult.first->second) + theProgram = theInsertResult.first->second->m_Shader; + + if (theProgram) { + if (theProgram->GetProgramType() == NVRenderShaderProgram::ProgramType::Graphics) { + if (theInsertResult.first->second) { + NVRenderContext &theContext(m_Context->GetRenderContext()); + theContext.SetActiveShader(theInsertResult.first->second->m_Shader); + } + + return *theInsertResult.first->second; + } else { + NVRenderContext &theContext(m_Context->GetRenderContext()); + theContext.SetActiveShader(theProgram); + return *(static_cast<NVRenderShaderProgram *>(theProgram)); + } + } + return SMaterialOrComputeShader(); + } + + void DoApplyInstanceValue(SCustomMaterial & /* inMaterial */, QT3DSU8 *inDataPtr, + CRegisteredString inPropertyName, + NVRenderShaderDataTypes::Enum inPropertyType, + NVRenderShaderProgram &inShader, + const SPropertyDefinition &inDefinition) + { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inPropertyName); + using namespace qt3ds::render; + if (theConstant) { + if (theConstant->GetShaderConstantType() == inPropertyType) { + if (inPropertyType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + StaticAssert<sizeof(CRegisteredString) + == sizeof(NVRenderTexture2DPtr)>::valid_expression(); + CRegisteredString *theStrPtr = reinterpret_cast<CRegisteredString *>(inDataPtr); + IBufferManager &theBufferManager(m_Context->GetBufferManager()); + IOffscreenRenderManager &theOffscreenRenderer( + m_Context->GetOffscreenRenderManager()); + NVRenderTexture2D *theTexture = nullptr; + + if (theStrPtr->IsValid()) { + if (theOffscreenRenderer.HasOffscreenRenderer(*theStrPtr)) { + SOffscreenRenderResult theResult + = theOffscreenRenderer.GetRenderedItem(*theStrPtr); + theTexture = theResult.m_Texture; + if (theTexture) { + SetSubpresentation(inShader, inPropertyName, theTexture, + &inDefinition); + } + } else { + SImageTextureData theTextureData + = theBufferManager.LoadRenderImage(*theStrPtr); + theTexture = theTextureData.m_Texture; + if (theTexture) { + SetTexture(inShader, inPropertyName, theTexture, &inDefinition, + TextureNeedsMips(&inDefinition, theTexture)); + } + } + } + } else { + switch (inPropertyType) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(type) \ + case NVRenderShaderDataTypes::type: \ + inShader.SetPropertyValue(theConstant, *(reinterpret_cast<type *>(inDataPtr))); \ + break; + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + QT3DS_ASSERT(false); + break; + } + } + } else { + qCCritical(INVALID_OPERATION, + "CustomMaterial ApplyInstanceValue command datatype and " + "shader datatypes differ for property %s", + inPropertyName.c_str()); + QT3DS_ASSERT(false); + } + } + } + + void ApplyInstanceValue(SCustomMaterial &inMaterial, SMaterialClass &inClass, + NVRenderShaderProgram &inShader, const SApplyInstanceValue &inCommand) + { + // sanity check + if (inCommand.m_PropertyName.IsValid()) { + bool canGetData = + inCommand.m_ValueOffset + getSizeofShaderDataType(inCommand.m_ValueType) + <= inMaterial.m_DataSectionByteSize; + if (canGetData == false) { + QT3DS_ASSERT(false); + return; + } + QT3DSU8 *dataPtr = inMaterial.GetDataSectionBegin() + inCommand.m_ValueOffset; + const SPropertyDefinition *theDefinition = + inClass.m_Class->FindPropertyByName(inCommand.m_PropertyName); + if (theDefinition) + DoApplyInstanceValue(inMaterial, dataPtr, inCommand.m_PropertyName, + inCommand.m_ValueType, inShader, *theDefinition); + } else { + NVConstDataRef<SPropertyDefinition> theDefs = inClass.m_Class->GetProperties(); + for (QT3DSU32 idx = 0, end = theDefs.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theDefs[idx]); + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(theDefinition.m_Name); + + // This is fine, the property wasn't found and we continue, no problem. + if (!theConstant) + continue; + QT3DSU8 *dataPtr = inMaterial.GetDataSectionBegin() + theDefinition.m_Offset; + DoApplyInstanceValue(inMaterial, dataPtr, theDefinition.m_Name, + theDefinition.m_DataType, inShader, theDefinition); + } + } + } + + void ApplyBlending(const SApplyBlending &inCommand) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + + theContext.SetBlendingEnabled(true); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc = + qt3ds::render::NVRenderBlendFunctionArgument( + inCommand.m_SrcBlendFunc, inCommand.m_DstBlendFunc, inCommand.m_SrcBlendFunc, + inCommand.m_DstBlendFunc); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + theContext.SetBlendFunction(blendFunc); + theContext.SetBlendEquation(blendEqu); + } + + // we currently only bind a source texture + const NVRenderTexture2D *ApplyBufferValue(const SCustomMaterial &inMaterial, + NVRenderShaderProgram &inShader, + const SApplyBufferValue &inCommand, + const NVRenderTexture2D *inSourceTexture) + { + const NVRenderTexture2D *theTexture = NULL; + + if (inCommand.m_BufferName.IsValid()) { + QT3DSU32 bufferIdx = FindBuffer(inCommand.m_BufferName); + if (bufferIdx < m_AllocatedBuffers.size()) { + SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]); + theTexture = theEntry.m_Texture; + } else { + // we must have allocated the read target before + qCCritical(INTERNAL_ERROR, + "CustomMaterial: ApplyBufferValue: Failed to setup read target"); + QT3DS_ASSERT(false); + } + } else + theTexture = inSourceTexture; + + if (inCommand.m_ParamName.IsValid()) { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inCommand.m_ParamName); + + if (theConstant) { + if (theConstant->GetShaderConstantType() + != NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + qCCritical(INVALID_OPERATION, + "CustomMaterial %s: Binding buffer to parameter %s that is not a texture", + inMaterial.m_ClassName.c_str(), inCommand.m_ParamName.c_str()); + QT3DS_ASSERT(false); + } else { + SetTexture(inShader, inCommand.m_ParamName, + const_cast<NVRenderTexture2D *>(theTexture)); + } + } + } + + return theTexture; + } + + void AllocateBuffer(const SAllocateBuffer &inCommand, NVRenderFrameBuffer *inTarget) + { + STextureDetails theSourceTextureDetails; + NVRenderTexture2D *theTexture = NULL; + // get color attachment we always assume at location 0 + if (inTarget) { + NVRenderTextureOrRenderBuffer theSourceTexture = + inTarget->GetAttachment(NVRenderFrameBufferAttachments::Color0); + // we need a texture + if (theSourceTexture.HasTexture2D()) { + theSourceTextureDetails = theSourceTexture.GetTexture2D()->GetTextureDetails(); + } else { + qCCritical(INVALID_OPERATION, "CustomMaterial %s: Invalid source texture", + inCommand.m_Name.c_str()); + QT3DS_ASSERT(false); + return; + } + } else { + NVRenderContext &theContext = m_Context->GetRenderContext(); + // if we allocate a buffer based on the default target use viewport to get the dimension + NVRenderRect theViewport(theContext.GetViewport()); + theSourceTextureDetails.m_Height = theViewport.m_Height; + theSourceTextureDetails.m_Width = theViewport.m_Width; + } + + QT3DSU32 theWidth = (QT3DSU32)(theSourceTextureDetails.m_Width * inCommand.m_SizeMultiplier); + QT3DSU32 theHeight = (QT3DSU32)(theSourceTextureDetails.m_Height * inCommand.m_SizeMultiplier); + NVRenderTextureFormats::Enum theFormat = inCommand.m_Format; + if (theFormat == NVRenderTextureFormats::Unknown) + theFormat = theSourceTextureDetails.m_Format; + IResourceManager &theResourceManager(m_Context->GetResourceManager()); + // size intentionally requiried every loop; + QT3DSU32 bufferIdx = FindBuffer(inCommand.m_Name); + if (bufferIdx < m_AllocatedBuffers.size()) { + SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]); + STextureDetails theDetails = theEntry.m_Texture->GetTextureDetails(); + if (theDetails.m_Width == theWidth && theDetails.m_Height == theHeight + && theDetails.m_Format == theFormat) { + theTexture = theEntry.m_Texture; + } else { + ReleaseBuffer(bufferIdx); + } + } + + if (theTexture == NULL) { + NVRenderFrameBuffer *theFB(theResourceManager.AllocateFrameBuffer()); + NVRenderTexture2D *theTexture( + theResourceManager.AllocateTexture2D(theWidth, theHeight, theFormat)); + theTexture->SetMagFilter(inCommand.m_FilterOp); + theTexture->SetMinFilter( + static_cast<NVRenderTextureMinifyingOp::Enum>(inCommand.m_FilterOp)); + theTexture->SetTextureWrapS(inCommand.m_TexCoordOp); + theTexture->SetTextureWrapT(inCommand.m_TexCoordOp); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *theTexture); + m_AllocatedBuffers.push_back(SCustomMaterialBuffer( + inCommand.m_Name, *theFB, *theTexture, inCommand.m_BufferFlags)); + } + } + + NVRenderFrameBuffer *BindBuffer(const SCustomMaterial &inMaterial, const SBindBuffer &inCommand, + bool &outClearTarget, QT3DSVec2 &outDestSize) + { + NVRenderFrameBuffer *theBuffer = NULL; + NVRenderTexture2D *theTexture = NULL; + + // search for the buffer + QT3DSU32 bufferIdx = FindBuffer(inCommand.m_BufferName); + if (bufferIdx < m_AllocatedBuffers.size()) { + theBuffer = m_AllocatedBuffers[bufferIdx].m_FrameBuffer; + theTexture = m_AllocatedBuffers[bufferIdx].m_Texture; + } + + if (theBuffer == NULL) { + qCCritical(INVALID_OPERATION, "Effect %s: Failed to find buffer %s for bind", + inMaterial.m_ClassName.c_str(), inCommand.m_BufferName.c_str()); + QT3DS_ASSERT(false); + return NULL; + } + + if (theTexture) { + STextureDetails theDetails(theTexture->GetTextureDetails()); + m_Context->GetRenderContext().SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + outDestSize = QT3DSVec2((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height); + outClearTarget = inCommand.m_NeedsClear; + } + + return theBuffer; + } + + void computeScreenCoverage(SCustomMaterialRenderContext &inRenderContext, QT3DSI32 *xMin, + QT3DSI32 *yMin, QT3DSI32 *xMax, QT3DSI32 *yMax) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + TNVBounds2BoxPoints outPoints; + QT3DSVec4 projMin(QT3DS_MAX_REAL); + QT3DSVec4 projMax(-QT3DS_MAX_REAL); + + // get points + inRenderContext.m_Subset.m_Bounds.expand(outPoints); + for (QT3DSU32 idx = 0; idx < 8; ++idx) { + QT3DSVec4 homPoint(outPoints[idx], 1.0); + QT3DSVec4 projPoint = inRenderContext.m_ModelViewProjection.transform(homPoint); + projPoint /= projPoint.w; + + if (projMin.x > projPoint.x) + projMin.x = projPoint.x; + if (projMin.y > projPoint.y) + projMin.y = projPoint.y; + if (projMin.z > projPoint.z) + projMin.z = projPoint.z; + + if (projMax.x < projPoint.x) + projMax.x = projPoint.x; + if (projMax.y < projPoint.y) + projMax.y = projPoint.y; + if (projMax.z < projPoint.z) + projMax.z = projPoint.z; + } + + NVRenderRect theViewport(theContext.GetViewport()); + QT3DSI32 x1 = QT3DSI32(projMax.x * (theViewport.m_Width / 2) + + (theViewport.m_X + (theViewport.m_Width / 2))); + QT3DSI32 y1 = QT3DSI32(projMax.y * (theViewport.m_Height / 2) + + (theViewport.m_Y + (theViewport.m_Height / 2))); + + QT3DSI32 x2 = QT3DSI32(projMin.x * (theViewport.m_Width / 2) + + (theViewport.m_X + (theViewport.m_Width / 2))); + QT3DSI32 y2 = QT3DSI32(projMin.y * (theViewport.m_Height / 2) + + (theViewport.m_Y + (theViewport.m_Height / 2))); + + if (x1 > x2) { + *xMin = x2; + *xMax = x1; + } else { + *xMin = x1; + *xMax = x2; + } + if (y1 > y2) { + *yMin = y2; + *yMax = y1; + } else { + *yMin = y1; + *yMax = y2; + } + } + + void BlitFramebuffer(SCustomMaterialRenderContext &inRenderContext, + const SApplyBlitFramebuffer &inCommand, NVRenderFrameBuffer *inTarget) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + // we change the read/render targets here + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + // we may alter scissor + NVRenderContextScopedProperty<bool> theScissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + + if (inCommand.m_DestBufferName.IsValid()) { + QT3DSU32 bufferIdx = FindBuffer(inCommand.m_DestBufferName); + if (bufferIdx < m_AllocatedBuffers.size()) { + SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]); + theContext.SetRenderTarget(theEntry.m_FrameBuffer); + } else { + // we must have allocated the read target before + qCCritical(INTERNAL_ERROR, + "CustomMaterial: BlitFramebuffer: Failed to setup render target"); + QT3DS_ASSERT(false); + } + } else { + // our dest is the default render target + theContext.SetRenderTarget(inTarget); + } + + if (inCommand.m_SourceBufferName.IsValid()) { + QT3DSU32 bufferIdx = FindBuffer(inCommand.m_SourceBufferName); + if (bufferIdx < m_AllocatedBuffers.size()) { + SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]); + theContext.SetReadTarget(theEntry.m_FrameBuffer); + theContext.SetReadBuffer(NVReadFaces::Color0); + } else { + // we must have allocated the read target before + qCCritical(INTERNAL_ERROR, + "CustomMaterial: BlitFramebuffer: Failed to setup read target"); + QT3DS_ASSERT(false); + } + } else { + // our source is the default read target + // depending on what we render we assume color0 or back + theContext.SetReadTarget(inTarget); + NVReadFaces::Enum value = (inTarget) ? NVReadFaces::Color0 : NVReadFaces::Back; + theContext.SetReadBuffer(value); + } + + NVRenderRect theViewport(theContext.GetViewport()); + theContext.SetScissorTestEnabled(false); + + if (!m_UseFastBlits) { + // only copy sreen amount of pixels + QT3DSI32 xMin, yMin, xMax, yMax; + computeScreenCoverage(inRenderContext, &xMin, &yMin, &xMax, &yMax); + + // same dimension + theContext.BlitFramebuffer(xMin, yMin, xMax, yMax, xMin, yMin, xMax, yMax, + NVRenderClearValues::Color, + NVRenderTextureMagnifyingOp::Nearest); + } else { + // same dimension + theContext.BlitFramebuffer( + theViewport.m_X, theViewport.m_Y, theViewport.m_X + theViewport.m_Width, + theViewport.m_Y + theViewport.m_Height, theViewport.m_X, theViewport.m_Y, + theViewport.m_X + theViewport.m_Width, theViewport.m_Y + theViewport.m_Height, + NVRenderClearValues::Color, NVRenderTextureMagnifyingOp::Nearest); + } + } + + SLayerGlobalRenderProperties + GetLayerGlobalRenderProperties(SCustomMaterialRenderContext &inRenderContext) + { + const SLayer &theLayer = inRenderContext.m_Layer; + const SLayerRenderData &theData = inRenderContext.m_LayerData; + + return SLayerGlobalRenderProperties( + theLayer, const_cast<SCamera &>(inRenderContext.m_Camera), theData.m_CameraDirection, + inRenderContext.m_Lights, NVDataRef<QT3DSVec3>(), theData.m_ShadowMapManager.mPtr, + const_cast<NVRenderTexture2D *>(inRenderContext.m_DepthTexture), + const_cast<NVRenderTexture2D *>(inRenderContext.m_AOTexture), theLayer.m_LightProbe, + theLayer.m_LightProbe2, theLayer.m_ProbeHorizon, theLayer.m_ProbeBright, + theLayer.m_Probe2Window, theLayer.m_Probe2Pos, theLayer.m_Probe2Fade, + theLayer.m_ProbeFov); + } + + void RenderPass(SCustomMaterialRenderContext &inRenderContext, SCustomMaterialShader &inShader, + NVRenderTexture2D * /* inSourceTexture */ + , + NVRenderFrameBuffer *inFrameBuffer, bool inRenderTargetNeedsClear, + NVRenderInputAssembler &inAssembler, QT3DSU32 inCount, QT3DSU32 inOffset) + { + ICustomMaterialShaderGenerator &theMaterialGenerator( + m_Context->GetCustomMaterialShaderGenerator()); + + theMaterialGenerator.SetMaterialProperties( + *inShader.m_Shader, inRenderContext.m_Material, QT3DSVec2(1.0, 1.0), + inRenderContext.m_ModelViewProjection, inRenderContext.m_NormalMatrix, + inRenderContext.m_ModelMatrix, inRenderContext.m_FirstImage, inRenderContext.m_Opacity, + GetLayerGlobalRenderProperties(inRenderContext)); + + NVRenderContext &theContext(m_Context->GetRenderContext()); + theContext.SetRenderTarget(inFrameBuffer); + + QT3DSVec4 clearColor(0.0); + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + clearColor); + if (inRenderTargetNeedsClear) { + theContext.Clear(qt3ds::render::NVRenderClearValues::Color); + } + + // I think the prim type should always be fetched from the + // current mesh subset setup because there you get the actual draw mode + // for this frame + NVRenderDrawMode::Enum theDrawMode = inAssembler.GetPrimitiveType(); + + // tesselation + if (inRenderContext.m_Subset.m_PrimitiveType == NVRenderDrawMode::Patches) { + QT3DSVec2 camProps(inRenderContext.m_Camera.m_ClipNear, + inRenderContext.m_Camera.m_ClipFar); + theDrawMode = inRenderContext.m_Subset.m_PrimitiveType; + inShader.m_Tessellation.m_EdgeTessLevel.Set(inRenderContext.m_Subset.m_EdgeTessFactor); + inShader.m_Tessellation.m_InsideTessLevel.Set( + inRenderContext.m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + inShader.m_Tessellation.m_PhongBlend.Set(0.75); + // this should finally be based on some user input + inShader.m_Tessellation.m_DistanceRange.Set(camProps); + // enable culling + inShader.m_Tessellation.m_DisableCulling.Set(0.0); + } + + if (inRenderContext.m_Subset.m_WireframeMode) { + NVRenderRect theViewport(theContext.GetViewport()); + QT3DSMat44 vpMatrix; + vpMatrix.column0 = QT3DSVec4((float)theViewport.m_Width / 2.0f, 0.0, 0.0, 0.0); + vpMatrix.column1 = QT3DSVec4(0.0, (float)theViewport.m_Height / 2.0f, 0.0, 0.0); + vpMatrix.column2 = QT3DSVec4(0.0, 0.0, 1.0, 0.0); + vpMatrix.column3 = + QT3DSVec4((float)theViewport.m_Width / 2.0f + (float)theViewport.m_X, + (float)theViewport.m_Height / 2.0f + (float)theViewport.m_Y, 0.0, 1.0); + + inShader.m_ViewportMatrix.Set(vpMatrix); + } + + theContext.SetInputAssembler(&inAssembler); + + theContext.SetCullingEnabled(true); + QT3DSU32 count = inCount; + QT3DSU32 offset = inOffset; + + theContext.Draw(theDrawMode, count, offset); + } + + void DoRenderCustomMaterial(SCustomMaterialRenderContext &inRenderContext, + const SCustomMaterial &inMaterial, SMaterialClass &inClass, + NVRenderFrameBuffer *inTarget, TShaderFeatureSet inFeatureSet) + { + NVRenderContext &theContext = m_Context->GetRenderContext(); + SCustomMaterialShader *theCurrentShader(NULL); + + NVRenderFrameBuffer *theCurrentRenderTarget(inTarget); + NVRenderRect theOriginalViewport(theContext.GetViewport()); + NVRenderTexture2D *theCurrentSourceTexture = 0; + + // for refrative materials we come from the transparent render path + // but we do not want to do blending + bool wasBlendingEnabled = theContext.IsBlendingEnabled(); + if (inMaterial.m_hasRefraction) + theContext.SetBlendingEnabled(false); + + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + + QT3DSVec2 theDestSize; + bool theRenderTargetNeedsClear = false; + + NVConstDataRef<dynamic::SCommand *> theCommands(inClass.m_Class->GetRenderCommands()); + for (QT3DSU32 commandIdx = 0, commandEnd = theCommands.size(); commandIdx < commandEnd; + ++commandIdx) { + const SCommand &theCommand(*theCommands[commandIdx]); + + switch (theCommand.m_Type) { + case CommandTypes::AllocateBuffer: + AllocateBuffer(static_cast<const SAllocateBuffer &>(theCommand), inTarget); + break; + case CommandTypes::BindBuffer: + theCurrentRenderTarget = + BindBuffer(inMaterial, static_cast<const SBindBuffer &>(theCommand), + theRenderTargetNeedsClear, theDestSize); + break; + case CommandTypes::BindTarget: + // Restore the previous render target and info. + theCurrentRenderTarget = inTarget; + theContext.SetViewport(theOriginalViewport); + break; + case CommandTypes::BindShader: { + theCurrentShader = NULL; + SMaterialOrComputeShader theBindResult = + BindShader(inRenderContext, inMaterial, + static_cast<const SBindShader &>(theCommand), inFeatureSet); + if (theBindResult.IsMaterialShader()) + theCurrentShader = &theBindResult.MaterialShader(); + } break; + case CommandTypes::ApplyInstanceValue: + // we apply the property update explicitly at the render pass + break; + case CommandTypes::Render: + if (theCurrentShader) { + RenderPass(inRenderContext, *theCurrentShader, theCurrentSourceTexture, + theCurrentRenderTarget, theRenderTargetNeedsClear, + *inRenderContext.m_Subset.m_InputAssembler, + inRenderContext.m_Subset.m_Count, inRenderContext.m_Subset.m_Offset); + } + // reset + theRenderTargetNeedsClear = false; + break; + case CommandTypes::ApplyBlending: + ApplyBlending(static_cast<const SApplyBlending &>(theCommand)); + break; + case CommandTypes::ApplyBufferValue: + if (theCurrentShader) + ApplyBufferValue(inMaterial, *theCurrentShader->m_Shader, + static_cast<const SApplyBufferValue &>(theCommand), + theCurrentSourceTexture); + break; + case CommandTypes::ApplyBlitFramebuffer: + BlitFramebuffer(inRenderContext, + static_cast<const SApplyBlitFramebuffer &>(theCommand), inTarget); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + if (inMaterial.m_hasRefraction) + theContext.SetBlendingEnabled(wasBlendingEnabled); + + // Release any per-frame buffers + for (QT3DSU32 idx = 0; idx < m_AllocatedBuffers.size(); ++idx) { + if (m_AllocatedBuffers[idx].m_Flags.IsSceneLifetime() == false) { + ReleaseBuffer(idx); + --idx; + } + } + } + + const char *GetShaderName(const SCustomMaterial &inMaterial) override + { + SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName); + if (!theClass) + return NULL; + + NVConstDataRef<dynamic::SCommand *> theCommands = theClass->m_Class->GetRenderCommands(); + TShaderAndFlags thePrepassShader; + for (QT3DSU32 idx = 0, end = theCommands.size(); + idx < end && thePrepassShader.first.mPtr == NULL; ++idx) { + const SCommand &theCommand = *theCommands[idx]; + if (theCommand.m_Type == CommandTypes::BindShader) { + const SBindShader &theBindCommand = static_cast<const SBindShader &>(theCommand); + return theBindCommand.m_ShaderPath.c_str(); + } + } + + QT3DS_ASSERT(false); + return NULL; + } + + void ApplyShaderPropertyValues(const SCustomMaterial &inMaterial, + NVRenderShaderProgram &inProgram) override + { + SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName); + if (!theClass) + return; + + SApplyInstanceValue applier; + ApplyInstanceValue(const_cast<SCustomMaterial &>(inMaterial), *theClass, inProgram, + applier); + } + + void renderSubpresentations(SCustomMaterial &inMaterial) override + { + SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName); + if (!theClass) + return; + + NVConstDataRef<SPropertyDefinition> theDefs = theClass->m_Class->GetProperties(); + for (QT3DSU32 idx = 0, end = theDefs.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theDefs[idx]); + if (theDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + QT3DSU8 *dataPtr = inMaterial.GetDataSectionBegin() + theDefinition.m_Offset; + StaticAssert<sizeof(CRegisteredString) + == sizeof(NVRenderTexture2DPtr)>::valid_expression(); + CRegisteredString *theStrPtr = reinterpret_cast<CRegisteredString *>(dataPtr); + IOffscreenRenderManager &theOffscreenRenderer( + m_Context->GetOffscreenRenderManager()); + + if (theStrPtr->IsValid()) { + if (theOffscreenRenderer.HasOffscreenRenderer(*theStrPtr)) + theOffscreenRenderer.GetRenderedItem(*theStrPtr); + } + } + } + } + + virtual void PrepareTextureForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial) + { + NVConstDataRef<SPropertyDefinition> thePropDefs = inClass.m_Class->GetProperties(); + for (QT3DSU32 idx = 0, end = thePropDefs.size(); idx < end; ++idx) { + if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + if (thePropDefs[idx].m_TexUsageType == NVRenderTextureTypeValue::Displace) { + SImage *pImage = NULL; + + // we only do this to not miss if "None" is selected + CRegisteredString theStrPtr = *reinterpret_cast<CRegisteredString *>( + inMaterial.GetDataSectionBegin() + thePropDefs[idx].m_Offset); + + if (theStrPtr.IsValid()) { + + QT3DSU32 index = FindAllocatedImage(thePropDefs[idx].m_ImagePath); + if (index == QT3DSU32(-1)) { + pImage = QT3DS_NEW(m_CoreContext.GetAllocator(), SImage)(); + m_AllocatedImages.push_back( + eastl::make_pair(thePropDefs[idx].m_ImagePath, pImage)); + } else + pImage = m_AllocatedImages[index].second; + + if (inMaterial.m_DisplacementMap != pImage) { + inMaterial.m_DisplacementMap = pImage; + inMaterial.m_DisplacementMap->m_ImagePath = + thePropDefs[idx].m_ImagePath; + inMaterial.m_DisplacementMap->m_ImageShaderName = + thePropDefs[idx].m_Name; // this is our name in the shader + inMaterial.m_DisplacementMap->m_VerticalTilingMode = + thePropDefs[idx].m_CoordOp; + inMaterial.m_DisplacementMap->m_HorizontalTilingMode = + thePropDefs[idx].m_CoordOp; + } + } else { + inMaterial.m_DisplacementMap = NULL; + } + } else if (thePropDefs[idx].m_TexUsageType == NVRenderTextureTypeValue::Emissive2) { + SImage *pImage = NULL; + + // we only do this to not miss if "None" is selected + CRegisteredString theStrPtr = *reinterpret_cast<CRegisteredString *>( + inMaterial.GetDataSectionBegin() + thePropDefs[idx].m_Offset); + + if (theStrPtr.IsValid()) { + QT3DSU32 index = FindAllocatedImage(thePropDefs[idx].m_ImagePath); + if (index == QT3DSU32(-1)) { + pImage = QT3DS_NEW(m_CoreContext.GetAllocator(), SImage)(); + m_AllocatedImages.push_back( + eastl::make_pair(thePropDefs[idx].m_ImagePath, pImage)); + } else + pImage = m_AllocatedImages[index].second; + + if (inMaterial.m_EmissiveMap2 != pImage) { + inMaterial.m_EmissiveMap2 = pImage; + inMaterial.m_EmissiveMap2->m_ImagePath = thePropDefs[idx].m_ImagePath; + inMaterial.m_EmissiveMap2->m_ImageShaderName = + thePropDefs[idx].m_Name; // this is our name in the shader + inMaterial.m_EmissiveMap2->m_VerticalTilingMode = + thePropDefs[idx].m_CoordOp; + inMaterial.m_EmissiveMap2->m_HorizontalTilingMode = + thePropDefs[idx].m_CoordOp; + } + } else { + inMaterial.m_EmissiveMap2 = NULL; + } + } + } + } + } + + virtual void PrepareDisplacementForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial) + { + if (inMaterial.m_DisplacementMap == NULL) + return; + + // our displacement mappin in MDL has fixed naming + NVConstDataRef<SPropertyDefinition> thePropDefs = inClass.m_Class->GetProperties(); + for (QT3DSU32 idx = 0, end = thePropDefs.size(); idx < end; ++idx) { + if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::QT3DSF32 + && AreEqual(thePropDefs[idx].m_Name.c_str(), "displaceAmount")) { + QT3DSF32 theValue = *reinterpret_cast<const QT3DSF32 *>(inMaterial.GetDataSectionBegin() + + thePropDefs[idx].m_Offset); + inMaterial.m_DisplaceAmount = theValue; + } else if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::QT3DSVec3 + && AreEqual(thePropDefs[idx].m_Name.c_str(), "displace_tiling")) { + QT3DSVec3 theValue = *reinterpret_cast<const QT3DSVec3 *>(inMaterial.GetDataSectionBegin() + + thePropDefs[idx].m_Offset); + if (theValue.x != inMaterial.m_DisplacementMap->m_Scale.x + || theValue.y != inMaterial.m_DisplacementMap->m_Scale.y) { + inMaterial.m_DisplacementMap->m_Scale = QT3DSVec2(theValue.x, theValue.y); + inMaterial.m_DisplacementMap->m_Flags.SetTransformDirty(true); + } + } + } + } + + void PrepareMaterialForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial) + { + PrepareTextureForRender(inClass, inMaterial); + + if (inClass.m_HasDisplacement) + PrepareDisplacementForRender(inClass, inMaterial); + } + + // Returns true if the material is dirty and thus will produce a different render result + // than previously. This effects things like progressive AA. + // TODO - return more information, specifically about transparency (object is transparent, + // object is completely transparent + bool PrepareForRender(const SModel & /*inModel*/, const SRenderSubset & /*inSubset*/, + SCustomMaterial &inMaterial, bool clearMaterialDirtyFlags) override + { + SMaterialClass *theMaterialClass = GetMaterialClass(inMaterial.m_ClassName); + if (theMaterialClass == NULL) { + QT3DS_ASSERT(false); + return false; + } + + PrepareMaterialForRender(*theMaterialClass, inMaterial); + + inMaterial.m_hasTransparency = theMaterialClass->m_HasTransparency; + inMaterial.m_hasRefraction = theMaterialClass->m_HasRefraction; + inMaterial.m_hasVolumetricDF = false; + + bool wasDirty = inMaterial.IsDirty() || theMaterialClass->m_AlwaysDirty; + if (clearMaterialDirtyFlags) + inMaterial.UpdateDirtyForFrame(); + + return wasDirty; + } + + // TODO - handle UIC specific features such as vertex offsets for prog-aa and opacity. + void RenderSubset(SCustomMaterialRenderContext &inRenderContext, + TShaderFeatureSet inFeatureSet) override + { + SMaterialClass *theClass = GetMaterialClass(inRenderContext.m_Material.m_ClassName); + + // Ensure that our overall render context comes back no matter what the client does. + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendFunctionArgument> + __blendFunction(m_Context->GetRenderContext(), &NVRenderContext::GetBlendFunction, + &NVRenderContext::SetBlendFunction, + qt3ds::render::NVRenderBlendFunctionArgument()); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendEquationArgument> + __blendEquation(m_Context->GetRenderContext(), &NVRenderContext::GetBlendEquation, + &NVRenderContext::SetBlendEquation, + qt3ds::render::NVRenderBlendEquationArgument()); + + NVRenderContextScopedProperty<bool> theBlendEnabled(m_Context->GetRenderContext(), + &NVRenderContext::IsBlendingEnabled, + &NVRenderContext::SetBlendingEnabled); + + DoRenderCustomMaterial(inRenderContext, inRenderContext.m_Material, *theClass, + m_Context->GetRenderContext().GetRenderTarget(), inFeatureSet); + } + + bool RenderDepthPrepass(const QT3DSMat44 &inMVP, const SCustomMaterial &inMaterial, + const SRenderSubset &inSubset) override + { + SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName); + NVConstDataRef<dynamic::SCommand *> theCommands = theClass->m_Class->GetRenderCommands(); + TShaderAndFlags thePrepassShader; + for (QT3DSU32 idx = 0, end = theCommands.size(); + idx < end && thePrepassShader.first.mPtr == NULL; ++idx) { + const SCommand &theCommand = *theCommands[idx]; + if (theCommand.m_Type == CommandTypes::BindShader) { + const SBindShader &theBindCommand = static_cast<const SBindShader &>(theCommand); + thePrepassShader = m_Context->GetDynamicObjectSystem().GetDepthPrepassShader( + theBindCommand.m_ShaderPath, CRegisteredString(), TShaderFeatureSet()); + } + } + + if (thePrepassShader.first.mPtr == NULL) + return false; + + NVRenderContext &theContext = m_Context->GetRenderContext(); + NVRenderShaderProgram &theProgram = *thePrepassShader.first; + theContext.SetActiveShader(&theProgram); + theProgram.SetPropertyValue("model_view_projection", inMVP); + theContext.SetInputAssembler(inSubset.m_InputAssemblerPoints); + theContext.Draw(NVRenderDrawMode::Lines, inSubset.m_PosVertexBuffer->GetNumVertexes(), 0); + return true; + } + + void OnMaterialActivationChange(const SCustomMaterial &inMaterial, bool inActive) override + { + Q_UNUSED(inMaterial) + Q_UNUSED(inActive) + } + + void EndFrame() override + { + QT3DSU64 currentFrameTime = qt3ds::foundation::Time::getCurrentTimeInTensOfNanoSeconds(); + if (m_LastFrameTime) { + QT3DSU64 timePassed = currentFrameTime - m_LastFrameTime; + m_MillisecondsSinceLastFrame = static_cast<QT3DSF32>(timePassed / 100000.0); + } + m_LastFrameTime = currentFrameTime; + } + + void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t * /*inProjectDir*/) const override + { + QT3DSU32 offset = ioBuffer.size(); + ioBuffer.write((QT3DSU32)m_StringMaterialMap.size()); + for (TStringMaterialMap::const_iterator iter = m_StringMaterialMap.begin(), + end = m_StringMaterialMap.end(); + iter != end; ++iter) { + size_t nameOffset = ioBuffer.size() - offset; + (void)nameOffset; + CRegisteredString materialName(iter->first); + materialName.Remap(inRemapMap); + ioBuffer.write(materialName); + const SMaterialClass *materialClass = iter->second.mPtr; + QT3DSU32 offset = ioBuffer.size(); + ioBuffer.write(*materialClass); + QT3DSU8 *materialOffset = ioBuffer.begin() + offset; + SMaterialClass *writtenClass = (SMaterialClass *)materialOffset; + writtenClass->AfterWrite(); + ioBuffer.align(4); + } + } + + void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t * /*inProjectDir*/) override + { + m_Allocator.m_PreAllocatedBlock = inData; + m_Allocator.m_OwnsMemory = false; + qt3ds::render::SDataReader theReader(inData.begin(), inData.end()); + QT3DSU32 numMaterialClasses = theReader.LoadRef<QT3DSU32>(); + for (QT3DSU32 idx = 0; idx < numMaterialClasses; ++idx) { + CRegisteredString clsName = theReader.LoadRef<CRegisteredString>(); + clsName.Remap(inStrDataBlock); + IDynamicObjectClass *theDynamicCls = + m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(clsName); + SMaterialClass *theReadClass = theReader.Load<SMaterialClass>(); + theReader.Align(4); + if (theDynamicCls) { + theReadClass->AfterRead(m_Allocator, *theDynamicCls); + m_StringMaterialMap.insert(eastl::make_pair(clsName, theReadClass)); + } + } + } + + ICustomMaterialSystem &GetCustomMaterialSystem(IQt3DSRenderContext &inContext) override + { + m_Context = &inContext; + + // check for fast blits + NVRenderContext &theContext = m_Context->GetRenderContext(); + m_UseFastBlits = theContext.GetRenderBackendCap( + qt3ds::render::NVRenderBackend::NVRenderBackendCaps::FastBlits); + + return *this; + } +}; +} + +ICustomMaterialSystemCore & +ICustomMaterialSystemCore::CreateCustomMaterialSystemCore(IQt3DSRenderContextCore &ctx) +{ + return *QT3DS_NEW(ctx.GetAllocator(), SMaterialSystem)(ctx); +} + diff --git a/src/runtimerender/Qt3DSRenderCustomMaterialSystem.h b/src/runtimerender/Qt3DSRenderCustomMaterialSystem.h new file mode 100644 index 0000000..6fe522f --- /dev/null +++ b/src/runtimerender/Qt3DSRenderCustomMaterialSystem.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CUSTOM_MATERIAL_SYSTEM_H +#define QT3DS_RENDER_CUSTOM_MATERIAL_SYSTEM_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "rendererimpl/Qt3DSVertexPipelineImpl.h" + +namespace qt3ds { +namespace render { + + namespace dynamic { + struct SCommand; // UICRenderEffectCommands.h + } + + struct SCustomMaterialRenderContext; + + class ICustomMaterialSystemCore : public NVRefCounted + { + public: + virtual bool IsMaterialRegistered(CRegisteredString inStr) = 0; + + virtual bool + RegisterMaterialClass(CRegisteredString inName, + NVConstDataRef<dynamic::SPropertyDeclaration> inProperties) = 0; + + virtual NVConstDataRef<dynamic::SPropertyDefinition> + GetCustomMaterialProperties(CRegisteredString inCustomMaterialName) const = 0; + + virtual void SetCustomMaterialRefraction(CRegisteredString inName, + bool inHasRefraction) = 0; + virtual void SetCustomMaterialTransparency(CRegisteredString inName, + bool inHasTransparency) = 0; + virtual void SetCustomMaterialAlwaysDirty(CRegisteredString inName, + bool inIsAlwaysDirty) = 0; + virtual void SetCustomMaterialShaderKey(CRegisteredString inName, QT3DSU32 inShaderKey) = 0; + virtual void SetCustomMaterialLayerCount(CRegisteredString inName, QT3DSU32 inLayerCount) = 0; + // The custom material commands are the actual commands that run for a given material + // effect. The tell the system exactly + // explicitly things like bind this shader, bind this render target, apply this property, + // run this shader + // See UICRenderEffectCommands.h for the list of commands. + // These commands are copied into the effect. + virtual void SetCustomMaterialCommands(CRegisteredString inName, + NVConstDataRef<dynamic::SCommand *> inCommands) = 0; + + virtual void SetMaterialClassShader(CRegisteredString inName, const char8_t *inShaderType, + const char8_t *inShaderVersion, + const char8_t *inShaderData, bool inHasGeomShader, + bool inIsComputeShader) = 0; + + virtual SCustomMaterial * + CreateCustomMaterial(CRegisteredString inName, + NVAllocatorCallback &inSceneGraphAllocator) = 0; + + virtual void SetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) = 0; + + virtual void SetPropertyTextureSettings(CRegisteredString inEffectName, + CRegisteredString inPropName, + CRegisteredString inPropPath, + NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, + NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) = 0; + + virtual void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t *inProjectDir) const = 0; + virtual void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) = 0; + + virtual ICustomMaterialSystem &GetCustomMaterialSystem(IQt3DSRenderContext &inContext) = 0; + + static ICustomMaterialSystemCore & + CreateCustomMaterialSystemCore(IQt3DSRenderContextCore &inContext); + }; + // How to handle blend modes? + class ICustomMaterialSystem : public ICustomMaterialSystemCore + { + public: + // Returns true if the material is dirty and thus will produce a different render result + // than previously. This effects things like progressive AA. + virtual bool PrepareForRender(const SModel &inModel, const SRenderSubset &inSubset, + SCustomMaterial &inMaterial, bool inClearDirty) = 0; + + virtual bool RenderDepthPrepass(const QT3DSMat44 &inMVP, const SCustomMaterial &inMaterial, + const SRenderSubset &inSubset) = 0; + virtual void RenderSubset(SCustomMaterialRenderContext &inRenderContext, + TShaderFeatureSet inFeatureSet) = 0; + virtual void OnMaterialActivationChange(const SCustomMaterial &inMaterial, + bool inActive) = 0; + + // get shader name + virtual const char *GetShaderName(const SCustomMaterial &inMaterial) = 0; + // apply property values + virtual void ApplyShaderPropertyValues(const SCustomMaterial &inMaterial, + NVRenderShaderProgram &inProgram) = 0; + virtual void renderSubpresentations(SCustomMaterial &inMaterial) = 0; + // Called by the uiccontext so this system can clear any per-frame render information. + virtual void EndFrame() = 0; + }; + + struct QT3DS_AUTOTEST_EXPORT SCustomMaterialVertexPipeline : public SVertexPipelineImpl + { + IQt3DSRenderContext *m_Context; + TessModeValues::Enum m_TessMode; + + SCustomMaterialVertexPipeline(IQt3DSRenderContext *inContext, TessModeValues::Enum inTessMode); + void InitializeTessControlShader(); + void InitializeTessEvaluationShader(); + void FinalizeTessControlShader(); + void FinalizeTessEvaluationShader(); + + // Responsible for beginning all vertex and fragment generation (void main() { etc). + virtual void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override; + // The fragment shader expects a floating point constant, object_opacity to be defined + // post this method. + virtual void BeginFragmentGeneration() override; + // Output variables may be mangled in some circumstances so the shader generation + // system needs an abstraction mechanism around this. + virtual void AssignOutput(const char8_t *inVarName, const char8_t *inVarValue) override; + virtual void GenerateEnvMapReflection() override {} + virtual void GenerateViewVector() override {} + virtual void GenerateUVCoords(QT3DSU32 inUVSet) override; + virtual void GenerateWorldNormal() override; + virtual void GenerateObjectNormal() override; + virtual void GenerateVarTangentAndBinormal() override; + virtual void GenerateWorldPosition() override; + // responsible for closing all vertex and fragment generation + virtual void EndVertexGeneration(bool customShader) override; + virtual void EndFragmentGeneration(bool customShader) override; + virtual IShaderStageGenerator &ActiveStage() override; + virtual void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override; + virtual void DoGenerateUVCoords(QT3DSU32 inUVSet) override; + virtual void DoGenerateWorldNormal() override; + virtual void DoGenerateObjectNormal() override; + virtual void DoGenerateWorldPosition() override; + virtual void DoGenerateVarTangentAndBinormal() override; + virtual void DoGenerateVertexColor() override; + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp b/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp new file mode 100644 index 0000000..221f329 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp @@ -0,0 +1,1931 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSRenderImage.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShadowMap.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderLightConstantProperties.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +namespace { + +const QT3DSF32 MINATTENUATION = 0; +const QT3DSF32 MAXATTENUATION = 1000; + +QT3DSF32 ClampFloat(QT3DSF32 value, QT3DSF32 min, QT3DSF32 max) +{ + return value < min ? min : ((value > max) ? max : value); +} + +QT3DSF32 TranslateConstantAttenuation(QT3DSF32 attenuation) +{ + return attenuation * .01f; +} + +QT3DSF32 TranslateLinearAttenuation(QT3DSF32 attenuation) +{ + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0001f; +} + +QT3DSF32 TranslateQuadraticAttenuation(QT3DSF32 attenuation) +{ + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0000001f; +} + +/** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ +struct SShaderTextureProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + NVRenderCachedShaderProperty<QT3DSVec2> m_Size; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + const char *sizeName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + , m_Size(sizeName, inShader) + { + } + SShaderTextureProperties() {} +}; + +/** + * Cached light property lookups, used one per light so a shader generator for N + * lights will have an array of N of these lookup objects. + */ +struct SShaderLightProperties +{ + // Color of the light + QT3DSVec4 m_LightColor; + SLightSourceShader m_LightData; + + SShaderLightProperties() {} +}; + +struct SShadowMapProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_ShadowmapTexture; ///< shadow texture + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_ShadowCubeTexture; ///< shadow cubemap + NVRenderCachedShaderProperty<QT3DSMat44> + m_ShadowmapMatrix; ///< world to ligh space transform matrix + NVRenderCachedShaderProperty<QT3DSVec4> m_ShadowmapSettings; ///< shadow rendering settings + + SShadowMapProperties() {} + SShadowMapProperties(const char *shadowmapTextureName, const char *shadowcubeTextureName, + const char *shadowmapMatrixName, const char *shadowmapSettingsName, + NVRenderShaderProgram &inShader) + : m_ShadowmapTexture(shadowmapTextureName, inShader) + , m_ShadowCubeTexture(shadowcubeTextureName, inShader) + , m_ShadowmapMatrix(shadowmapMatrixName, inShader) + , m_ShadowmapSettings(shadowmapSettingsName, inShader) + { + } +}; + +/** + * The results of generating a shader. Caches all possible variable names into + * typesafe objects. + */ +struct SShaderGeneratorGeneratedShader +{ + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + // Specific properties we know the shader has to have. + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + NVRenderCachedShaderProperty<QT3DSMat33> m_NormalMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_GlobalTransform; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewProj; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialDiffuse; + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialProperties; + // tint, ior + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialSpecular; + NVRenderCachedShaderProperty<QT3DSF32> m_BumpAmount; + NVRenderCachedShaderProperty<QT3DSF32> m_DisplaceAmount; + NVRenderCachedShaderProperty<QT3DSF32> m_TranslucentFalloff; + NVRenderCachedShaderProperty<QT3DSF32> m_DiffuseLightWrap; + NVRenderCachedShaderProperty<QT3DSF32> m_FresnelPower; + NVRenderCachedShaderProperty<QT3DSVec3> m_DiffuseColor; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + QT3DSVec3 m_LightAmbientTotal; + NVRenderCachedShaderProperty<QT3DSVec3> m_MaterialDiffuseLightAmbientTotal; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AOTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeProps; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOpts; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeRot; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOfs; + NVRenderCachedShaderProperty<QT3DSVec2> m_LightProbeSize; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe2; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbe2Props; + NVRenderCachedShaderProperty<QT3DSVec2> m_LightProbe2Size; + + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_LightsBuffer; + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_lightConstantProperties; + + // Cache the image property name lookups + nvvector<SShaderTextureProperties> m_Images; + nvvector<SShaderLightProperties> m_Lights; + // Cache shadow map properties + nvvector<SShadowMapProperties> m_ShadowMaps; + + QT3DSI32 m_RefCount; + + SShaderGeneratorGeneratedShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_NormalMatrix("normal_matrix", inShader) + , m_GlobalTransform("model_matrix", inShader) + , m_ViewProj("view_projection_matrix", inShader) + , m_ViewMatrix("view_matrix", inShader) + , m_MaterialDiffuse("material_diffuse", inShader) + , m_MaterialProperties("material_properties", inShader) + , m_MaterialSpecular("material_specular", inShader) + , m_BumpAmount("bumpAmount", inShader) + , m_DisplaceAmount("displaceAmount", inShader) + , m_TranslucentFalloff("translucentFalloff", inShader) + , m_DiffuseLightWrap("diffuseLightWrap", inShader) + , m_FresnelPower("fresnelPower", inShader) + , m_DiffuseColor("diffuse_color", inShader) + , m_CameraPosition("camera_position", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_MaterialDiffuseLightAmbientTotal("light_ambient_total", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_AOTexture("ao_sampler", inShader) + , m_LightProbe("light_probe", inShader) + , m_LightProbeProps("light_probe_props", inShader) + , m_LightProbeOpts("light_probe_opts", inShader) + , m_LightProbeRot("light_probe_rotation", inShader) + , m_LightProbeOfs("light_probe_offset", inShader) + , m_LightProbeSize("light_probe_size", inShader) + , m_LightProbe2("light_probe2", inShader) + , m_LightProbe2Props("light_probe2_props", inShader) + , m_LightProbe2Size("light_probe2_size", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_LightsBuffer("cbBufferLights", inShader) + , m_lightConstantProperties(NULL) + , m_Images(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Images") + , m_Lights(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Lights") + , m_ShadowMaps(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_ShadowMaps") + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~SShaderGeneratorGeneratedShader() + { + if (m_lightConstantProperties) + delete m_lightConstantProperties; + m_Shader.release(); + } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } +}; + + +#ifndef EA_PLATFORM_WINDOWS +#define _snprintf snprintf +#endif + +struct SShaderGenerator : public IDefaultMaterialShaderGenerator +{ + typedef Qt3DSString TStrType; + typedef nvhash_map<NVRenderShaderProgram *, NVScopedRefCounted<SShaderGeneratorGeneratedShader>> + TProgramToShaderMap; + typedef qt3ds::foundation::nvhash_map<CRegisteredString, + NVScopedRefCounted<qt3ds::render::NVRenderConstantBuffer>> + TStrConstanBufMap; + + IQt3DSRenderContext &m_RenderContext; + IShaderProgramGenerator &m_ProgramGenerator; + + const SDefaultMaterial *m_CurrentMaterial; + SShaderDefaultMaterialKey *m_CurrentKey; + Qt3DSShadowMap *m_ShadowMapManager; + IDefaultMaterialVertexPipeline *m_CurrentPipeline; + TShaderFeatureSet m_CurrentFeatureSet; + NVDataRef<SLight *> m_Lights; + SRenderableImage *m_FirstImage; + bool m_HasTransparency; + bool m_LightsAsSeparateUniforms; + + TStrType m_ImageStem; + TStrType m_ImageSampler; + TStrType m_ImageOffsets; + TStrType m_ImageRotations; + TStrType m_ImageFragCoords; + TStrType m_ImageTemp; + TStrType m_ImageSamplerSize; + + TStrType m_TexCoordTemp; + + TStrType m_LightStem; + TStrType m_LightColor; + TStrType m_LightSpecularColor; + TStrType m_LightAttenuation; + TStrType m_LightConstantAttenuation; + TStrType m_LightLinearAttenuation; + TStrType m_LightQuadraticAttenuation; + TStrType m_NormalizedDirection; + TStrType m_LightDirection; + TStrType m_LightPos; + TStrType m_LightUp; + TStrType m_LightRt; + TStrType m_RelativeDistance; + TStrType m_RelativeDirection; + + TStrType m_ShadowMapStem; + TStrType m_ShadowCubeStem; + TStrType m_ShadowMatrixStem; + TStrType m_ShadowCoordStem; + TStrType m_ShadowControlStem; + + TStrType m_TempStr; + + eastl::string m_GeneratedShaderString; + + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + TProgramToShaderMap m_ProgramToShaderMap; + + TStrConstanBufMap m_ConstantBuffers; ///< store all constants buffers + + QT3DSI32 m_RefCount; + + SShaderGenerator(IQt3DSRenderContext &inRc) + : m_RenderContext(inRc) + , m_ProgramGenerator(m_RenderContext.GetShaderProgramGenerator()) + , m_CurrentMaterial(NULL) + , m_CurrentKey(NULL) + , m_ShadowMapManager(NULL) + , m_CurrentPipeline(NULL) + , m_FirstImage(NULL) + , m_LightsAsSeparateUniforms(false) + , m_ProgramToShaderMap(inRc.GetAllocator(), "m_ProgramToShaderMap") + , m_ConstantBuffers(inRc.GetAllocator(), "m_ConstantBuffers") + , m_RefCount(0) + { + } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + m_ConstantBuffers.clear(); + NVDelete(m_RenderContext.GetAllocator(), this); + } + } + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IDefaultMaterialVertexPipeline &VertexGenerator() { return *m_CurrentPipeline; } + IShaderStageGenerator &FragmentGenerator() + { + return *m_ProgramGenerator.GetStage(ShaderGeneratorStages::Fragment); + } + SShaderDefaultMaterialKey &Key() { return *m_CurrentKey; } + const SDefaultMaterial &Material() { return *m_CurrentMaterial; } + TShaderFeatureSet FeatureSet() { return m_CurrentFeatureSet; } + bool HasTransparency() { return m_HasTransparency; } + + void addFunction(IShaderStageGenerator &generator, QString functionName) + { + generator.AddFunction(functionName); + } + + void SetupImageVariableNames(size_t imageIdx) + { + m_ImageStem = "image"; + char buf[16]; + _snprintf(buf, 16, "%d", int(imageIdx)); + m_ImageStem.append(buf); + m_ImageStem.append("_"); + + m_ImageSampler = m_ImageStem; + m_ImageSampler.append("sampler"); + m_ImageOffsets = m_ImageStem; + m_ImageOffsets.append("offsets"); + m_ImageRotations = m_ImageStem; + m_ImageRotations.append("rotations"); + m_ImageFragCoords = m_ImageStem; + m_ImageFragCoords.append("uv_coords"); + m_ImageSamplerSize = m_ImageStem; + m_ImageSamplerSize.append("size"); + } + + void SetupTexCoordVariableName(size_t uvSet) + { + m_TexCoordTemp = "varTexCoord"; + char buf[16]; + _snprintf(buf, 16, "%d", int(uvSet)); + m_TexCoordTemp.append(buf); + } + + SImageVariableNames GetImageVariableNames(QT3DSU32 inIdx) override + { + SetupImageVariableNames(inIdx); + SImageVariableNames retval; + retval.m_ImageSampler = m_ImageSampler.c_str(); + retval.m_ImageFragCoords = m_ImageFragCoords.c_str(); + return retval; + } + + void AddLocalVariable(IShaderStageGenerator &inGenerator, const char8_t *inName, + const char8_t *inType) + { + inGenerator << "\t" << inType << " " << inName << ";" << Endl; + } + + void AddLocalVariable(IShaderStageGenerator &inGenerator, const TStrType &inName, + const char8_t *inType) + { + AddLocalVariable(inGenerator, inName.c_str(), inType); + } + + void GenerateImageUVCoordinates(IShaderStageGenerator &inVertexPipeline, QT3DSU32 idx, QT3DSU32 uvSet, + SRenderableImage &image) override + { + IDefaultMaterialVertexPipeline &vertexShader( + static_cast<IDefaultMaterialVertexPipeline &>(inVertexPipeline)); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + SetupImageVariableNames(idx); + SetupTexCoordVariableName(uvSet); + fragmentShader.AddUniform(m_ImageSampler, "sampler2D"); + vertexShader.AddUniform(m_ImageOffsets, "vec3"); + fragmentShader.AddUniform(m_ImageOffsets, "vec3"); + vertexShader.AddUniform(m_ImageRotations, "vec4"); + fragmentShader.AddUniform(m_ImageRotations, "vec4"); + + if (image.m_Image.m_MappingMode == ImageMappingModes::Normal) { + vertexShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " + << m_ImageRotations << ".y, " << m_ImageOffsets << ".x );" << Endl; + vertexShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " + << m_ImageRotations << ".w, " << m_ImageOffsets << ".y );" << Endl; + vertexShader.AddOutgoing(m_ImageFragCoords, "vec2"); + addFunction(vertexShader, "getTransformedUVCoords"); + vertexShader.GenerateUVCoords(uvSet); + m_ImageTemp = m_ImageFragCoords; + m_ImageTemp.append("temp"); + vertexShader << "\tvec2 " << m_ImageTemp << " = getTransformedUVCoords( vec3( " + << m_TexCoordTemp << ", 1.0), uTransform, vTransform );" << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + vertexShader << "\t" << m_ImageTemp << ".y = 1.0 - " << m_ImageFragCoords << ".y;" + << Endl; + + vertexShader.AssignOutput(m_ImageFragCoords.c_str(), m_ImageTemp.c_str()); + } else { + fragmentShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " + << m_ImageRotations << ".y, " << m_ImageOffsets << ".x );" << Endl; + fragmentShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " + << m_ImageRotations << ".w, " << m_ImageOffsets << ".y );" << Endl; + vertexShader.GenerateEnvMapReflection(); + addFunction(fragmentShader, "getTransformedUVCoords"); + fragmentShader << "\tvec2 " << m_ImageFragCoords + << " = getTransformedUVCoords( environment_map_reflection, uTransform, " + "vTransform );" + << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + fragmentShader << "\t" << m_ImageFragCoords << ".y = 1.0 - " << m_ImageFragCoords + << ".y;" << Endl; + } + } + + void GenerateImageUVCoordinates(QT3DSU32 idx, SRenderableImage &image, QT3DSU32 uvSet = 0) + { + GenerateImageUVCoordinates(VertexGenerator(), idx, uvSet, image); + } + + void GenerateImageUVCoordinates(QT3DSU32 idx, SRenderableImage &image, + IDefaultMaterialVertexPipeline &inShader) + { + if (image.m_Image.m_MappingMode == ImageMappingModes::Normal) { + SetupImageVariableNames(idx); + inShader.AddUniform(m_ImageSampler, "sampler2D"); + inShader.AddUniform(m_ImageOffsets, "vec3"); + inShader.AddUniform(m_ImageRotations, "vec4"); + + inShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " << m_ImageRotations + << ".y, " << m_ImageOffsets << ".x );" << Endl; + inShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " << m_ImageRotations + << ".w, " << m_ImageOffsets << ".y );" << Endl; + inShader << "\tvec2 " << m_ImageFragCoords << ";" << Endl; + addFunction(inShader, "getTransformedUVCoords"); + inShader.GenerateUVCoords(); + inShader + << "\t" << m_ImageFragCoords + << " = getTransformedUVCoords( vec3( varTexCoord0, 1.0), uTransform, vTransform );" + << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + inShader << "\t" << m_ImageFragCoords << ".y = 1.0 - " << m_ImageFragCoords << ".y;" + << Endl; + } + } + + void OutputSpecularEquation(DefaultMaterialSpecularModel::Enum inSpecularModel, + IShaderStageGenerator &fragmentShader, const char *inLightDir, + const char *inLightSpecColor) + { + switch (inSpecularModel) { + case DefaultMaterialSpecularModel::KGGX: { + fragmentShader.AddInclude("defaultMaterialPhysGlossyBSDF.glsllib"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * kggxGlossyDefaultMtl( " + << "world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor + << ".rgb, vec3(material_specular.xyz), roughnessAmount, " + "roughnessAmount ).rgb;" + << Endl; + } break; + case DefaultMaterialSpecularModel::KWard: { + fragmentShader.AddInclude("defaultMaterialPhysGlossyBSDF.glsllib"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * wardGlossyDefaultMtl( " + << "world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor + << ".rgb, vec3(material_specular.xyz), roughnessAmount, " + "roughnessAmount ).rgb;" + << Endl; + } break; + default: + addFunction(fragmentShader, "specularBSDF"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * specularBSDF( " + << "world_normal, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor << ".rgb, 1.0, 2.56 / (roughnessAmount + " + "0.01), vec3(1.0), scatter_reflect ).rgb;" + << Endl; + break; + } + } + + void OutputDiffuseAreaLighting(IShaderStageGenerator &infragmentShader, const char *inPos, + TStrType inLightPrefix) + { + m_NormalizedDirection = inLightPrefix + "_areaDir"; + AddLocalVariable(infragmentShader, m_NormalizedDirection, "vec3"); + infragmentShader << "\tlightAttenuation = calculateDiffuseAreaOld( " << m_LightDirection + << ".xyz, " << m_LightPos << ".xyz, " << m_LightUp << ", " << m_LightRt + << ", " << inPos << ", " << m_NormalizedDirection << " );" << Endl; + } + + void OutputSpecularAreaLighting(IShaderStageGenerator &infragmentShader, const char *inPos, + const char *inView, const char *inLightSpecColor) + { + addFunction(infragmentShader, "sampleAreaGlossyDefault"); + infragmentShader.AddUniform("material_specular", "vec4"); + infragmentShader << "global_specular_light.rgb += " << inLightSpecColor + << ".rgb * lightAttenuation * shadowFac * material_specular.rgb * " + "specularAmount * sampleAreaGlossyDefault( tanFrame, " + << inPos << ", " << m_NormalizedDirection << ", " << m_LightPos << ".xyz, " + << m_LightRt << ".w, " << m_LightUp << ".w, " << inView + << ", roughnessAmount, roughnessAmount ).rgb;" << Endl; + } + + void AddTranslucencyIrradiance(IShaderStageGenerator &infragmentShader, SRenderableImage *image, + TStrType inLightPrefix, bool areaLight) + { + if (image == NULL) + return; + + addFunction(infragmentShader, "diffuseReflectionWrapBSDF"); + if (areaLight) { + infragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "translucent_thickness_exp * diffuseReflectionWrapBSDF( " + "-world_normal, " + << m_NormalizedDirection << ", " << m_LightColor + << ".rgb, diffuseLightWrap ).rgb;" << Endl; + } else { + infragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "translucent_thickness_exp * diffuseReflectionWrapBSDF( " + "-world_normal, " + << "-" << m_NormalizedDirection << ", " << m_LightColor + << ".rgb, diffuseLightWrap ).rgb;" << Endl; + } + } + + void SetupShadowMapVariableNames(size_t lightIdx) + { + m_ShadowMapStem = "shadowmap"; + m_ShadowCubeStem = "shadowcube"; + char buf[16]; + _snprintf(buf, 16, "%d", int(lightIdx)); + m_ShadowMapStem.append(buf); + m_ShadowCubeStem.append(buf); + m_ShadowMatrixStem = m_ShadowMapStem; + m_ShadowMatrixStem.append("_matrix"); + m_ShadowCoordStem = m_ShadowMapStem; + m_ShadowCoordStem.append("_coord"); + m_ShadowControlStem = m_ShadowMapStem; + m_ShadowControlStem.append("_control"); + } + + void AddShadowMapContribution(IShaderStageGenerator &inLightShader, QT3DSU32 lightIndex, + RenderLightTypes::Enum inType) + { + SetupShadowMapVariableNames(lightIndex); + + inLightShader.AddInclude("shadowMapping.glsllib"); + if (inType == RenderLightTypes::Directional) { + inLightShader.AddUniform(m_ShadowMapStem, "sampler2D"); + } else { + inLightShader.AddUniform(m_ShadowCubeStem, "samplerCube"); + } + inLightShader.AddUniform(m_ShadowControlStem, "vec4"); + inLightShader.AddUniform(m_ShadowMatrixStem, "mat4"); + + /* + if ( inType == RenderLightTypes::Area ) + { + inLightShader << "vec2 " << m_ShadowCoordStem << ";" << Endl; + inLightShader << "\tshadow_map_occl = sampleParaboloid( " << m_ShadowMapStem << ", " + << m_ShadowControlStem << ", " + << + m_ShadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z), " + << m_ShadowCoordStem + << " );" << Endl; + } + else */ + if (inType != RenderLightTypes::Directional) { + inLightShader << "\tshadow_map_occl = sampleCubemap( " << m_ShadowCubeStem << ", " + << m_ShadowControlStem << ", " << m_ShadowMatrixStem << ", " << m_LightPos + << ".xyz, varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z) );" + << Endl; + } else + inLightShader << "\tshadow_map_occl = sampleOrthographic( " << m_ShadowMapStem << ", " + << m_ShadowControlStem << ", " << m_ShadowMatrixStem + << ", varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z) );" << Endl; + } + + void AddDisplacementMappingForDepthPass(IShaderStageGenerator &inShader) override + { + inShader.AddIncoming("attr_uv0", "vec2"); + inShader.AddIncoming("attr_norm", "vec3"); + inShader.AddUniform("displacementSampler", "sampler2D"); + inShader.AddUniform("displaceAmount", "float"); + inShader.AddUniform("displacementMap_rot", "vec4"); + inShader.AddUniform("displacementMap_offset", "vec3"); + inShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + + inShader.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, displacementMap_rot.y, " + "displacementMap_offset.x );"); + inShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, displacementMap_rot.w, " + "displacementMap_offset.y );"); + addFunction(inShader, "getTransformedUVCoords"); + inShader.Append("\tvec2 uv_coords = attr_uv0;"); + inShader << "\tuv_coords = getTransformedUVCoords( vec3( uv_coords, 1.0), uTransform, " + "vTransform );\n"; + inShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, uv_coords , attr_norm, attr_pos );" + << Endl; + inShader.Append("\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } + + void AddDisplacementImageUniforms(IShaderStageGenerator &inGenerator, + QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + if (displacementImage) { + SetupImageVariableNames(displacementImageIdx); + inGenerator.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + inGenerator.AddUniform("model_matrix", "mat4"); + inGenerator.AddUniform("camera_position", "vec3"); + inGenerator.AddUniform("displaceAmount", "float"); + inGenerator.AddUniform(m_ImageSampler, "sampler2D"); + } + } + + bool MaybeAddMaterialFresnel(IShaderStageGenerator &fragmentShader, NVConstDataRef<QT3DSU32> inKey, + bool inFragmentHasSpecularAmount) + { + if (m_DefaultMaterialShaderKeyProperties.m_FresnelEnabled.GetValue(inKey)) { + if (inFragmentHasSpecularAmount == false) + fragmentShader << "\tfloat specularAmount = 1.0;" << Endl; + inFragmentHasSpecularAmount = true; + fragmentShader.AddInclude("defaultMaterialFresnel.glsllib"); + fragmentShader.AddUniform("fresnelPower", "float"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tfloat fresnelRatio = defaultMaterialSimpleFresnel( world_normal, " + "view_vector, material_specular.w, fresnelPower );" + << Endl; + fragmentShader << "\tspecularAmount *= fresnelRatio;" << Endl; + } + return inFragmentHasSpecularAmount; + } + void SetupLightVariableNames(size_t lightIdx, SLight &inLight) + { + if (m_LightsAsSeparateUniforms) { + char buf[16]; + _snprintf(buf, 16, "light_%d", int(lightIdx)); + m_LightStem = buf; + m_LightColor = m_LightStem; + m_LightColor.append("_diffuse"); + m_LightDirection = m_LightStem; + m_LightDirection.append("_direction"); + m_LightSpecularColor = m_LightStem; + m_LightSpecularColor.append("_specular"); + if (inLight.m_LightType == RenderLightTypes::Point) { + m_LightPos = m_LightStem; + m_LightPos.append("_position"); + m_LightAttenuation = m_LightStem; + m_LightAttenuation.append("_attenuation"); + } else if (inLight.m_LightType == RenderLightTypes::Area) { + m_LightPos = m_LightStem; + m_LightPos.append("_position"); + m_LightUp = m_LightStem; + m_LightUp.append("_up"); + m_LightRt = m_LightStem; + m_LightRt.append("_right"); + } + } else { + m_LightStem = "lights"; + char buf[16]; + _snprintf(buf, 16, "[%d].", int(lightIdx)); + m_LightStem.append(buf); + + m_LightColor = m_LightStem; + m_LightColor.append("diffuse"); + m_LightDirection = m_LightStem; + m_LightDirection.append("direction"); + m_LightSpecularColor = m_LightStem; + m_LightSpecularColor.append("specular"); + if (inLight.m_LightType == RenderLightTypes::Point) { + m_LightPos = m_LightStem; + m_LightPos.append("position"); + m_LightConstantAttenuation = m_LightStem; + m_LightConstantAttenuation.append("constantAttenuation"); + m_LightLinearAttenuation = m_LightStem; + m_LightLinearAttenuation.append("linearAttenuation"); + m_LightQuadraticAttenuation = m_LightStem; + m_LightQuadraticAttenuation.append("quadraticAttenuation"); + } else if (inLight.m_LightType == RenderLightTypes::Area) { + m_LightPos = m_LightStem; + m_LightPos.append("position"); + m_LightUp = m_LightStem; + m_LightUp.append("up"); + m_LightRt = m_LightStem; + m_LightRt.append("right"); + } + } + } + + void addDisplacementMapping(IDefaultMaterialVertexPipeline &inShader) + { + inShader.AddIncoming("attr_uv0", "vec2"); + inShader.AddIncoming("attr_norm", "vec3"); + inShader.AddUniform("displacementSampler", "sampler2D"); + inShader.AddUniform("displaceAmount", "float"); + inShader.AddUniform("displacementMap_rot", "vec4"); + inShader.AddUniform("displacementMap_offset", "vec3"); + inShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + + inShader.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, displacementMap_rot.y, " + "displacementMap_offset.x );"); + inShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, displacementMap_rot.w, " + "displacementMap_offset.y );"); + addFunction(inShader, "getTransformedUVCoords"); + inShader.GenerateUVCoords(); + inShader << "\tvarTexCoord0 = getTransformedUVCoords( vec3( varTexCoord0, 1.0), " + "uTransform, vTransform );\n"; + inShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, varTexCoord0 , attr_norm, attr_pos );" + << Endl; + inShader.Append("\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } + + void GenerateTextureSwizzle(NVRenderTextureSwizzleMode::Enum swizzleMode, + eastl::basic_string<char8_t> &texSwizzle, + eastl::basic_string<char8_t> &lookupSwizzle) + { + qt3ds::render::NVRenderContextType deprecatedContextFlags(NVRenderContextValues::GL2 + | NVRenderContextValues::GLES2); + + if (!(m_RenderContext.GetRenderContext().GetRenderContextType() & deprecatedContextFlags)) { + switch (swizzleMode) { + case NVRenderTextureSwizzleMode::L8toR8: + case NVRenderTextureSwizzleMode::L16toR16: + texSwizzle.append(".rgb"); + lookupSwizzle.append(".rrr"); + break; + case NVRenderTextureSwizzleMode::L8A8toRG8: + texSwizzle.append(".rgba"); + lookupSwizzle.append(".rrrg"); + break; + case NVRenderTextureSwizzleMode::A8toR8: + texSwizzle.append(".a"); + lookupSwizzle.append(".r"); + break; + default: + break; + } + } + } + + ///< get the light constant buffer and generate if necessary + NVRenderConstantBuffer *GetLightConstantBuffer(QT3DSU32 inLightCount) + { + NVRenderContext &theContext(m_RenderContext.GetRenderContext()); + + // we assume constant buffer support + QT3DS_ASSERT(theContext.GetConstantBufferSupport()); + + // we only create if if we have lights + if (!inLightCount || !theContext.GetConstantBufferSupport()) + return NULL; + + CRegisteredString theName = theContext.GetStringTable().RegisterStr("cbBufferLights"); + NVRenderConstantBuffer *pCB = theContext.GetConstantBuffer(theName); + + if (!pCB) { + // create + SLightSourceShader s[QT3DS_MAX_NUM_LIGHTS]; + NVDataRef<QT3DSU8> cBuffer((QT3DSU8 *)&s, (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + + (4 * sizeof(QT3DSI32))); + pCB = theContext.CreateConstantBuffer( + theName, qt3ds::render::NVRenderBufferUsageType::Static, + (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + (4 * sizeof(QT3DSI32)), cBuffer); + if (!pCB) { + QT3DS_ASSERT(false); + return NULL; + } + // init first set + memset(&s[0], 0x0, sizeof(SLightSourceShader)); + QT3DSI32 cgLights = 0; + pCB->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + pCB->UpdateRaw(4 * sizeof(QT3DSI32), + NVDataRef<QT3DSU8>((QT3DSU8 *)&s[0], sizeof(SLightSourceShader))); + pCB->Update(); // update to hardware + + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + } + + return pCB; + } + + void SetImageShaderVariables(SShaderGeneratorGeneratedShader &inShader, + SRenderableImage &inImage, QT3DSU32 idx) + { + size_t numImageVariables = inShader.m_Images.size(); + for (size_t namesIdx = numImageVariables; namesIdx <= idx; ++namesIdx) { + SetupImageVariableNames(idx); + inShader.m_Images.push_back( + SShaderTextureProperties(m_ImageSampler.c_str(), m_ImageOffsets.c_str(), + m_ImageRotations.c_str(), m_ImageSamplerSize.c_str(), + inShader.m_Shader)); + } + SShaderTextureProperties &theShaderProps = inShader.m_Images[idx]; + const QT3DSMat44 &textureTransform = inImage.m_Image.m_TextureTransform; + // We separate rotational information from offset information so that just maybe the shader + // will attempt to push less information to the card. + const QT3DSF32 *dataPtr(textureTransform.front()); + // The third member of the offsets contains a flag indicating if the texture was + // premultiplied or not. + // We use this to mix the texture alpha. + QT3DSVec3 offsets(dataPtr[12], dataPtr[13], + inImage.m_Image.m_TextureData.m_TextureFlags.IsPreMultiplied() ? 1.0f + : 0.0f); + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + // The image horizontal and vertical tiling modes need to be set here, before we set texture + // on the shader. + // because setting the image on the texture forces the textue to bind and immediately apply + // any tex params. + NVRenderTexture2D *imageTexture = inImage.m_Image.m_TextureData.m_Texture; + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapS( + inImage.m_Image.m_HorizontalTilingMode); + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapT( + inImage.m_Image.m_VerticalTilingMode); + theShaderProps.m_Sampler.Set(imageTexture); + theShaderProps.m_Offsets.Set(offsets); + theShaderProps.m_Rotations.Set(rotations); + theShaderProps.m_Size.Set(QT3DSVec2(imageTexture->GetTextureDetails().m_Width, + imageTexture->GetTextureDetails().m_Height)); + } + + void GenerateShadowMapOcclusion(QT3DSU32 lightIdx, bool inShadowEnabled, + RenderLightTypes::Enum inType) + { + if (inShadowEnabled) { + VertexGenerator().GenerateWorldPosition(); + AddShadowMapContribution(FragmentGenerator(), lightIdx, inType); + /* + VertexGenerator().AddUniform( m_ShadowMatrixStem, "mat4" ); + VertexGenerator().AddOutgoing( m_ShadowCoordStem, "vec4" ); + VertexGenerator() << "\tvec4 local_" << m_ShadowCoordStem << " = " << m_ShadowMatrixStem + << " * vec4(local_model_world_position, 1.0);" << Endl; + m_TempStr.assign( "local_" ); + m_TempStr.append( m_ShadowCoordStem ); + VertexGenerator().AssignOutput( m_ShadowCoordStem.c_str(), m_TempStr.c_str() ); + */ + } else { + FragmentGenerator() << "\tshadow_map_occl = 1.0;" << Endl; + } + } + + void GenerateVertexShader() + { + // vertex displacement + QT3DSU32 imageIdx = 0; + SRenderableImage *displacementImage = NULL; + QT3DSU32 displacementImageIdx = 0; + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + if (img->m_MapType == ImageMapTypes::Displacement) { + displacementImage = img; + displacementImageIdx = imageIdx; + break; + } + } + + // the pipeline opens/closes up the shaders stages + VertexGenerator().BeginVertexGeneration(displacementImageIdx, displacementImage); + } + + void GenerateFragmentShader(SShaderDefaultMaterialKey &inKey) + { + bool specularEnabled = Material().IsSpecularEnabled(); + bool vertexColorsEnabled = Material().IsVertexColorsEnabled(); + + bool hasLighting = Material().HasLighting(); + bool hasImage = m_FirstImage != NULL; + + bool hasIblProbe = m_DefaultMaterialShaderKeyProperties.m_HasIbl.GetValue(inKey); + bool hasSpecMap = false; + bool hasEnvMap = false; + bool hasEmissiveMap = false; + bool hasLightmaps = false; + // Pull the bump out as + SRenderableImage *bumpImage = NULL; + QT3DSU32 imageIdx = 0; + QT3DSU32 bumpImageIdx = 0; + SRenderableImage *specularAmountImage = NULL; + QT3DSU32 specularAmountImageIdx = 0; + SRenderableImage *roughnessImage = NULL; + QT3DSU32 roughnessImageIdx = 0; + // normal mapping + SRenderableImage *normalImage = NULL; + QT3DSU32 normalImageIdx = 0; + // translucency map + SRenderableImage *translucencyImage = NULL; + QT3DSU32 translucencyImageIdx = 0; + // lightmaps + SRenderableImage *lightmapIndirectImage = NULL; + QT3DSU32 lightmapIndirectImageIdx = 0; + SRenderableImage *lightmapRadiosityImage = NULL; + QT3DSU32 lightmapRadiosityImageIdx = 0; + SRenderableImage *lightmapShadowImage = NULL; + QT3DSU32 lightmapShadowImageIdx = 0; + const bool supportStandardDerivatives + = m_RenderContext.GetRenderContext().IsStandardDerivativesSupported(); + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + hasSpecMap = img->m_MapType == ImageMapTypes::Specular; + if (img->m_MapType == ImageMapTypes::Bump) { + bumpImage = img; + bumpImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::SpecularAmountMap) { + specularAmountImage = img; + specularAmountImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Roughness) { + roughnessImage = img; + roughnessImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Normal) { + normalImage = img; + normalImageIdx = imageIdx; + } else if (img->m_Image.m_MappingMode == ImageMappingModes::Environment) { + hasEnvMap = true; + } else if (img->m_MapType == ImageMapTypes::Translucency) { + translucencyImage = img; + translucencyImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Emissive) { + hasEmissiveMap = true; + } else if (img->m_MapType == ImageMapTypes::LightmapIndirect) { + lightmapIndirectImage = img; + lightmapIndirectImageIdx = imageIdx; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapRadiosity) { + lightmapRadiosityImage = img; + lightmapRadiosityImageIdx = imageIdx; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapShadow) { + lightmapShadowImage = img; + lightmapShadowImageIdx = imageIdx; + hasLightmaps = true; + } + } + + bool enableFresnel = m_DefaultMaterialShaderKeyProperties.m_FresnelEnabled.GetValue(inKey); + bool enableSSAO = false; + bool enableSSDO = false; + bool enableShadowMaps = false; + bool enableBumpNormal = normalImage || bumpImage; + + for (QT3DSU32 idx = 0; idx < FeatureSet().size(); ++idx) { + eastl::string name(FeatureSet()[idx].m_Name.c_str()); + if (name == "QT3DS_ENABLE_SSAO") + enableSSAO = FeatureSet()[idx].m_Enabled; + else if (name == "QT3DS_ENABLE_SSDO") + enableSSDO = FeatureSet()[idx].m_Enabled; + else if (name == "QT3DS_ENABLE_SSM") + enableShadowMaps = FeatureSet()[idx].m_Enabled; + } + + bool includeSSAOSSDOVars = enableSSAO || enableSSDO || enableShadowMaps; + + VertexGenerator().BeginFragmentGeneration(); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + IDefaultMaterialVertexPipeline &vertexShader(VertexGenerator()); + + // The fragment or vertex shaders may not use the material_properties or diffuse + // uniforms in all cases but it is simpler to just add them and let the linker strip them. + fragmentShader.AddUniform("material_diffuse", "vec4"); + fragmentShader.AddUniform("diffuse_color", "vec3"); + fragmentShader.AddUniform("material_properties", "vec4"); + + // All these are needed for SSAO + if (includeSSAOSSDOVars) { + fragmentShader.AddInclude("SSAOCustomMaterial.glsllib"); + // fragmentShader.AddUniform( "ao_sampler", "sampler2D" ); + } + + if (hasIblProbe && hasLighting) { + fragmentShader.AddInclude("sampleProbe.glsllib"); + } + + if (hasLighting) { + if (!m_LightsAsSeparateUniforms) + addFunction(fragmentShader, "sampleLightVars"); + addFunction(fragmentShader, "diffuseReflectionBSDF"); + } + + if (hasLighting && hasLightmaps) { + fragmentShader.AddInclude("evalLightmaps.glsllib"); + } + + // view_vector, varWorldPos, world_normal are all used if there is a specular map + // in addition to if there is specular lighting. So they are lifted up here, always + // generated. + // we rely on the linker to strip out what isn't necessary instead of explicitly stripping + // it for code simplicity. + if (hasImage) { + fragmentShader.Append("\tvec3 uTransform;"); + fragmentShader.Append("\tvec3 vTransform;"); + } + + if (includeSSAOSSDOVars || hasSpecMap || hasLighting || hasEnvMap || enableFresnel + || hasIblProbe || enableBumpNormal) { + vertexShader.GenerateViewVector(); + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateWorldPosition(); + } + if (includeSSAOSSDOVars || specularEnabled || hasIblProbe || enableBumpNormal) + vertexShader.GenerateVarTangentAndBinormal(); + + if (vertexColorsEnabled) + vertexShader.GenerateVertexColor(); + else + fragmentShader.Append("\tvec3 vertColor = vec3(1.0);"); + + // You do bump or normal mapping but not both + if (bumpImage != NULL) { + GenerateImageUVCoordinates(bumpImageIdx, *bumpImage); + fragmentShader.AddUniform("bumpAmount", "float"); + + fragmentShader.AddUniform(m_ImageSamplerSize, "vec2"); + fragmentShader.AddInclude("defaultMaterialBumpNoLod.glsllib"); + fragmentShader << "\tworld_normal = defaultMaterialBumpNoLod( " << m_ImageSampler + << ", bumpAmount, " << m_ImageFragCoords + << ", tangent, binormal, world_normal, " + << m_ImageSamplerSize << ");" << Endl; + // Do gram schmidt + fragmentShader << "\tbinormal = normalize(cross(world_normal, tangent) );\n"; + fragmentShader << "\ttangent = normalize(cross(binormal, world_normal) );\n"; + + } else if (normalImage != NULL) { + GenerateImageUVCoordinates(normalImageIdx, *normalImage); + + fragmentShader.AddInclude("defaultMaterialFileNormalTexture.glsllib"); + fragmentShader.AddUniform("bumpAmount", "float"); + + fragmentShader << "\tworld_normal = defaultMaterialFileNormalTexture( " + << m_ImageSampler << ", bumpAmount, " << m_ImageFragCoords + << ", tangent, binormal );" << Endl; + } + + if (includeSSAOSSDOVars || specularEnabled || hasIblProbe || enableBumpNormal) + fragmentShader << "\tmat3 tanFrame = mat3(tangent, binormal, world_normal);" << Endl; + + bool fragmentHasSpecularAmount = false; + + if (hasEmissiveMap) { + fragmentShader.Append("\tvec3 global_emission = material_diffuse.rgb;"); + } + + if (hasLighting) { + fragmentShader.AddUniform("light_ambient_total", "vec3"); + + fragmentShader.Append( + "\tvec4 global_diffuse_light = vec4(light_ambient_total.xyz, 1.0);"); + fragmentShader.Append("\tvec3 global_specular_light = vec3(0.0, 0.0, 0.0);"); + fragmentShader.Append("\tfloat shadow_map_occl = 1.0;"); + + if (specularEnabled) { + vertexShader.GenerateViewVector(); + fragmentShader.AddUniform("material_properties", "vec4"); + } + + if (lightmapIndirectImage != NULL) { + GenerateImageUVCoordinates(lightmapIndirectImageIdx, *lightmapIndirectImage, 1); + fragmentShader << "\tvec4 indirect_light = texture2D( " << m_ImageSampler << ", " + << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tglobal_diffuse_light += indirect_light;" << Endl; + if (specularEnabled) { + fragmentShader + << "\tglobal_specular_light += indirect_light.rgb * material_properties.x;" + << Endl; + } + } + + if (lightmapRadiosityImage != NULL) { + GenerateImageUVCoordinates(lightmapRadiosityImageIdx, *lightmapRadiosityImage, 1); + fragmentShader << "\tvec4 direct_light = texture2D( " << m_ImageSampler << ", " + << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tglobal_diffuse_light += direct_light;" << Endl; + if (specularEnabled) { + fragmentShader + << "\tglobal_specular_light += direct_light.rgb * material_properties.x;" + << Endl; + } + } + + if (translucencyImage != NULL) { + fragmentShader.AddUniform("translucentFalloff", "float"); + fragmentShader.AddUniform("diffuseLightWrap", "float"); + + GenerateImageUVCoordinates(translucencyImageIdx, *translucencyImage); + + fragmentShader << "\tvec4 translucent_depth_range = texture2D( " << m_ImageSampler + << ", " << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tfloat translucent_thickness = translucent_depth_range.r * " + "translucent_depth_range.r;" + << Endl; + fragmentShader << "\tfloat translucent_thickness_exp = exp( translucent_thickness " + "* translucentFalloff);" + << Endl; + } + + fragmentShader.Append("\tfloat lightAttenuation = 1.0;"); + + AddLocalVariable(fragmentShader, "aoFactor", "float"); + + if (hasLighting && enableSSAO) + fragmentShader.Append("\taoFactor = customMaterialAO();"); + else + fragmentShader.Append("\taoFactor = 1.0;"); + + AddLocalVariable(fragmentShader, "shadowFac", "float"); + + if (specularEnabled) { + fragmentShader << "\tfloat specularAmount = material_properties.x;" << Endl; + fragmentHasSpecularAmount = true; + } + // Fragment lighting means we can perhaps attenuate the specular amount by a texture + // lookup. + + fragmentShader << "\tvec3 specularColor = vec3(1.0);" << Endl; + if (specularAmountImage) { + if (!specularEnabled) + fragmentShader << "\tfloat specularAmount = 1.0;" << Endl; + GenerateImageUVCoordinates(specularAmountImageIdx, *specularAmountImage); + fragmentShader << "\tspecularColor = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << " ).xyz;" << Endl; + fragmentHasSpecularAmount = true; + } + + fragmentShader << "\tfloat roughnessAmount = material_properties.y;" << Endl; + if (roughnessImage) { + GenerateImageUVCoordinates(roughnessImageIdx, *roughnessImage); + fragmentShader << "\tfloat sampledRoughness = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << " ).x;" << Endl; + //The roughness sampled from roughness textures is Disney roughness + //which has to be squared to get the proper value + fragmentShader << "\troughnessAmount = roughnessAmount * " + << "sampledRoughness * sampledRoughness;" << Endl; + } + + fragmentHasSpecularAmount = + MaybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount); + + // Iterate through all lights + for (QT3DSU32 lightIdx = 0; lightIdx < m_Lights.size(); ++lightIdx) { + SLight *lightNode = m_Lights[lightIdx]; + SetupLightVariableNames(lightIdx, *lightNode); + bool isDirectional = lightNode->m_LightType == RenderLightTypes::Directional; + bool isArea = lightNode->m_LightType == RenderLightTypes::Area; + bool isShadow = enableShadowMaps && lightNode->m_CastShadow; + + fragmentShader.Append(""); + char buf[10]; + sprintf(buf, "%d", lightIdx); + + m_TempStr.assign("light"); + m_TempStr.append(buf); + + fragmentShader << "\t//Light " << buf << Endl; + fragmentShader << "\tlightAttenuation = 1.0;" << Endl; + if (isDirectional) { + + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightDirection, "vec4"); + fragmentShader.AddUniform(m_LightColor, "vec4"); + } + + if (enableSSDO) { + fragmentShader << "\tshadowFac = customMaterialShadow( " << m_LightDirection + << ".xyz, varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = 1.0;" << Endl; + } + + GenerateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, + lightNode->m_LightType); + + if (specularEnabled && enableShadowMaps && isShadow) + fragmentShader << "\tlightAttenuation *= shadow_map_occl;" << Endl; + + fragmentShader << "\tglobal_diffuse_light.rgb += shadowFac * shadow_map_occl * " + "diffuseReflectionBSDF( world_normal, " + << "-" << m_LightDirection << ".xyz, view_vector, " + << m_LightColor << ".rgb, 0.0 ).rgb;" << Endl; + + if (specularEnabled) { + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularEquation(Material().m_SpecularModel, fragmentShader, + m_LightDirection.c_str(), + m_LightSpecularColor.c_str()); + } + } else if (isArea) { + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightColor, "vec4"); + fragmentShader.AddUniform(m_LightPos, "vec4"); + fragmentShader.AddUniform(m_LightDirection, "vec4"); + fragmentShader.AddUniform(m_LightUp, "vec4"); + fragmentShader.AddUniform(m_LightRt, "vec4"); + } else { + addFunction(fragmentShader, "areaLightVars"); + } + addFunction(fragmentShader, "calculateDiffuseAreaOld"); + vertexShader.GenerateWorldPosition(); + GenerateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, + lightNode->m_LightType); + + // Debug measure to make sure paraboloid sampling was projecting to the right + // location + // fragmentShader << "\tglobal_diffuse_light.rg += " << m_ShadowCoordStem << ";" + // << Endl; + m_NormalizedDirection = m_TempStr; + m_NormalizedDirection.append("_Frame"); + + AddLocalVariable(fragmentShader, m_NormalizedDirection, "mat3"); + fragmentShader << m_NormalizedDirection << " = mat3( " << m_LightRt << ".xyz, " + << m_LightUp << ".xyz, -" << m_LightDirection << ".xyz );" + << Endl; + + if (enableSSDO) { + fragmentShader << "\tshadowFac = shadow_map_occl * customMaterialShadow( " + << m_LightDirection << ".xyz, varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = shadow_map_occl;" << Endl; + } + + if (specularEnabled) { + vertexShader.GenerateViewVector(); + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularAreaLighting(fragmentShader, "varWorldPos", "view_vector", + m_LightSpecularColor.c_str()); + } + + OutputDiffuseAreaLighting(fragmentShader, "varWorldPos", m_TempStr); + fragmentShader << "\tlightAttenuation *= shadowFac;" << Endl; + + AddTranslucencyIrradiance(fragmentShader, translucencyImage, m_TempStr, true); + + fragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "diffuseReflectionBSDF( world_normal, " + << m_NormalizedDirection << ", view_vector, " << m_LightColor + << ".rgb, 0.0 ).rgb;" << Endl; + } else { + + vertexShader.GenerateWorldPosition(); + GenerateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, + lightNode->m_LightType); + + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightColor, "vec4"); + fragmentShader.AddUniform(m_LightPos, "vec4"); + } + + m_RelativeDirection = m_TempStr; + m_RelativeDirection.append("_relativeDirection"); + + m_NormalizedDirection = m_RelativeDirection; + m_NormalizedDirection.append("_normalized"); + + m_RelativeDistance = m_TempStr; + m_RelativeDistance.append("_distance"); + + fragmentShader << "\tvec3 " << m_RelativeDirection << " = varWorldPos - " + << m_LightPos << ".xyz;" << Endl; + fragmentShader << "\tfloat " << m_RelativeDistance << " = length( " + << m_RelativeDirection << " );" << Endl; + fragmentShader << "\tvec3 " << m_NormalizedDirection << " = " + << m_RelativeDirection << " / " << m_RelativeDistance << ";" + << Endl; + + if (enableSSDO) { + fragmentShader << "\tshadowFac = shadow_map_occl * customMaterialShadow( " + << m_NormalizedDirection << ", varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = shadow_map_occl;" << Endl; + } + + addFunction(fragmentShader, "calculatePointLightAttenuation"); + + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightAttenuation, "vec3"); + fragmentShader + << "\tlightAttenuation = shadowFac * calculatePointLightAttenuation(" + << "vec3( " << m_LightAttenuation << ".x, " << m_LightAttenuation + << ".y, " << m_LightAttenuation << ".z), " << m_RelativeDistance + << ");" << Endl; + } else { + fragmentShader + << "\tlightAttenuation = shadowFac * calculatePointLightAttenuation(" + << "vec3( " << m_LightConstantAttenuation << ", " + << m_LightLinearAttenuation << ", " << m_LightQuadraticAttenuation + << "), " << m_RelativeDistance << ");" + << Endl; + } + + + + AddTranslucencyIrradiance(fragmentShader, translucencyImage, m_TempStr, false); + + fragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "diffuseReflectionBSDF( world_normal, " + << "-" << m_NormalizedDirection << ", view_vector, " + << m_LightColor << ".rgb, 0.0 ).rgb;" << Endl; + + if (specularEnabled) { + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularEquation(Material().m_SpecularModel, fragmentShader, + m_NormalizedDirection.c_str(), + m_LightSpecularColor.c_str()); + } + } + } + + // This may be confusing but the light colors are already modulated by the base + // material color. + // Thus material color is the base material color * material emissive. + // Except material_color.a *is* the actual opacity factor. + // Furthermore object_opacity is something that may come from the vertex pipeline or + // somewhere else. + // We leave it up to the vertex pipeline to figure it out. + fragmentShader << "\tglobal_diffuse_light = vec4(global_diffuse_light.xyz * aoFactor, " + "object_opacity);" + << Endl << "\tglobal_specular_light = vec3(global_specular_light.xyz);" + << Endl; + } else // no lighting. + { + fragmentShader << "\tvec4 global_diffuse_light = vec4(0.0, 0.0, 0.0, object_opacity);" + << Endl << "\tvec3 global_specular_light = vec3(0.0, 0.0, 0.0);" << Endl; + + // We still have specular maps and such that could potentially use the fresnel variable. + fragmentHasSpecularAmount = + MaybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount); + } + + if (!hasEmissiveMap) + fragmentShader + << "\tglobal_diffuse_light.rgb += diffuse_color.rgb * material_diffuse.rgb;" + << Endl; + + // since we already modulate our material diffuse color + // into the light color we will miss it entirely if no IBL + // or light is used + if (hasLightmaps && !(m_Lights.size() || hasIblProbe)) + fragmentShader << "\tglobal_diffuse_light.rgb *= diffuse_color.rgb;" << Endl; + + if (hasLighting && hasIblProbe) { + vertexShader.GenerateWorldNormal(); + + fragmentShader << "\tglobal_diffuse_light.rgb += diffuse_color.rgb * aoFactor * " + "sampleDiffuse( tanFrame ).xyz;" + << Endl; + + if (specularEnabled) { + + fragmentShader.AddUniform("material_specular", "vec4"); + + fragmentShader << "\tglobal_specular_light.xyz += specularAmount * specularColor * " + "vec3(material_specular.xyz) * sampleGlossy( tanFrame, " + "view_vector, roughnessAmount ).xyz;" + << Endl; + } + } + + if (hasImage) { + fragmentShader.Append("\tvec4 texture_color;"); + QT3DSU32 idx = 0; + for (SRenderableImage *image = m_FirstImage; image; image = image->m_NextImage, ++idx) { + // Various maps are handled on a different locations + if (image->m_MapType == ImageMapTypes::Bump + || image->m_MapType == ImageMapTypes::Normal + || image->m_MapType == ImageMapTypes::Displacement + || image->m_MapType == ImageMapTypes::SpecularAmountMap + || image->m_MapType == ImageMapTypes::Roughness + || image->m_MapType == ImageMapTypes::Translucency + || image->m_MapType == ImageMapTypes::LightmapIndirect + || image->m_MapType == ImageMapTypes::LightmapRadiosity) { + continue; + } + + eastl::basic_string<char8_t> texSwizzle, lookupSwizzle, texLodStr; + + GenerateImageUVCoordinates(idx, *image, 0); + + GenerateTextureSwizzle( + image->m_Image.m_TextureData.m_Texture->GetTextureSwizzleMode(), texSwizzle, + lookupSwizzle); + + if (texLodStr.empty()) { + fragmentShader << "\ttexture_color" << texSwizzle.c_str() << " = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << ")" + << lookupSwizzle.c_str() << ";" << Endl; + } else { + fragmentShader << "\ttexture_color" << texSwizzle.c_str() << "= textureLod( " + << m_ImageSampler << ", " << m_ImageFragCoords << ", " + << texLodStr.c_str() << " )" << lookupSwizzle.c_str() << ";" + << Endl; + } + + if (image->m_Image.m_TextureData.m_TextureFlags.IsPreMultiplied() == true) + fragmentShader << "\ttexture_color.rgb = texture_color.a > 0.0 ? " + "texture_color.rgb / texture_color.a : vec3( 0, 0, 0 );" + << Endl; + + // These mapping types honestly don't make a whole ton of sense to me. + switch (image->m_MapType) { + case ImageMapTypes::Diffuse: // assume images are premultiplied. + case ImageMapTypes::LightmapShadow: + // We use image offsets.z to switch between incoming premultiplied textures or + // not premultiplied textures. + // If Z is 1, then we assume the incoming texture is already premultiplied, else + // we just read the rgb value. + fragmentShader.Append("\tglobal_diffuse_light *= texture_color;"); + break; + case ImageMapTypes::Specular: + + fragmentShader.AddUniform("material_specular", "vec4"); + if (fragmentHasSpecularAmount) { + fragmentShader.Append("\tglobal_specular_light.xyz += specularAmount * " + "specularColor * texture_color.xyz * " + "material_specular.xyz;"); + } else { + fragmentShader.Append("\tglobal_specular_light.xyz += texture_color.xyz * " + "material_specular.xyz;"); + } + fragmentShader.Append("\tglobal_diffuse_light.a *= texture_color.a;"); + break; + case ImageMapTypes::Opacity: + fragmentShader.Append("\tglobal_diffuse_light.a *= texture_color.a;"); + break; + case ImageMapTypes::Emissive: + fragmentShader.Append( + "\tglobal_emission *= texture_color.xyz * texture_color.a;"); + break; + default: + QT3DS_ASSERT(false); // fallthrough intentional + } + } + } + + if (hasEmissiveMap) { + fragmentShader.Append("\tglobal_diffuse_light.rgb += global_emission.rgb;"); + } + + // Ensure the rgb colors are in range. + fragmentShader.Append("\tfragOutput = vec4( clamp( vertColor * global_diffuse_light.xyz + " + "global_specular_light.xyz, 0.0, 65519.0 ), global_diffuse_light.a " + ");"); + + if (VertexGenerator().HasActiveWireframe()) { + fragmentShader.Append("vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;"); + fragmentShader.Append( + "\tfloat d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);"); + fragmentShader.Append("\tfloat mixVal = smoothstep(0.0, 1.0, d);"); // line width 1.0 + + fragmentShader.Append( + "\tfragOutput = mix( vec4(0.0, 1.0, 0.0, 1.0), fragOutput, mixVal);"); + } + } + + NVRenderShaderProgram *GenerateMaterialShader(const char8_t *inShaderPrefix) + { + // build a string that allows us to print out the shader we are generating to the log. + // This is time consuming but I feel like it doesn't happen all that often and is very + // useful to users + // looking at the log file. + + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(nonNull(inShaderPrefix)); + + SShaderDefaultMaterialKey theKey(Key()); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + + m_LightsAsSeparateUniforms = !m_RenderContext.GetRenderContext().GetConstantBufferSupport(); + + GenerateVertexShader(); + GenerateFragmentShader(theKey); + + VertexGenerator().EndVertexGeneration(false); + VertexGenerator().EndFragmentGeneration(false); + + return ProgramGenerator().CompileGeneratedShader(m_GeneratedShaderString.c_str(), + SShaderCacheProgramFlags(), FeatureSet()); + } + + virtual NVRenderShaderProgram * + GenerateShader(const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, + bool inHasTransparency, const char8_t *inVertexPipelineName, const char8_t *) override + { + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::DefaultMaterial); + m_CurrentMaterial = static_cast<const SDefaultMaterial *>(&inMaterial); + m_CurrentKey = &inShaderDescription; + m_CurrentPipeline = static_cast<IDefaultMaterialVertexPipeline *>(&inVertexPipeline); + m_CurrentFeatureSet = inFeatureSet; + m_Lights = inLights; + m_FirstImage = inFirstImage; + m_HasTransparency = inHasTransparency; + + return GenerateMaterialShader(inVertexPipelineName); + } + + SShaderGeneratorGeneratedShader &GetShaderForProgram(NVRenderShaderProgram &inProgram) + { + eastl::pair<TProgramToShaderMap::iterator, bool> inserter = + m_ProgramToShaderMap.insert(eastl::make_pair( + &inProgram, NVScopedRefCounted<SShaderGeneratorGeneratedShader>(NULL))); + if (inserter.second) { + NVAllocatorCallback &alloc(m_RenderContext.GetRenderContext().GetAllocator()); + inserter.first->second = QT3DS_NEW(alloc, SShaderGeneratorGeneratedShader)( + inProgram, m_RenderContext.GetRenderContext()); + } + return *inserter.first->second; + } + + void SetGlobalProperties(NVRenderShaderProgram &inProgram, const SLayer & /*inLayer*/ + , + SCamera &inCamera, QT3DSVec3 inCameraDirection, + NVDataRef<SLight *> inLights, NVDataRef<QT3DSVec3> inLightDirections, + Qt3DSShadowMap *inShadowMapManager) + { + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(inProgram)); + m_RenderContext.GetRenderContext().SetActiveShader(&inProgram); + + m_ShadowMapManager = inShadowMapManager; + + SCamera &theCamera(inCamera); + shader.m_CameraPosition.Set(theCamera.GetGlobalPos()); + shader.m_CameraDirection.Set(inCameraDirection); + + QT3DSMat44 viewProj; + if (shader.m_ViewProj.IsValid()) { + theCamera.CalculateViewProjectionMatrix(viewProj); + shader.m_ViewProj.Set(viewProj); + } + + if (shader.m_ViewMatrix.IsValid()) { + viewProj = theCamera.m_GlobalTransform.getInverse(); + shader.m_ViewMatrix.Set(viewProj); + } + + // update the constant buffer + shader.m_AoShadowParams.Set(); + // We can't cache light properties because they can change per object. + QT3DSVec4 theLightAmbientTotal = QT3DSVec4(0, 0, 0, 1); + size_t numShaderLights = shader.m_Lights.size(); + size_t numShadowLights = shader.m_ShadowMaps.size(); + for (QT3DSU32 lightIdx = 0, shadowMapIdx = 0, lightEnd = inLights.size(); + lightIdx < lightEnd && lightIdx < QT3DS_MAX_NUM_LIGHTS; ++lightIdx) { + SLight *theLight(inLights[lightIdx]); + if (lightIdx >= numShaderLights) { + shader.m_Lights.push_back(SShaderLightProperties()); + ++numShaderLights; + } + if (shadowMapIdx >= numShadowLights && numShadowLights < QT3DS_MAX_NUM_SHADOWS) { + if (theLight->m_Scope == NULL && theLight->m_CastShadow) { + // PKC TODO : Fix multiple shadow issues. + // Need to know when the list of lights changes order, and clear shadow maps + // when that happens. + SetupShadowMapVariableNames(lightIdx); + shader.m_ShadowMaps.push_back(SShadowMapProperties( + m_ShadowMapStem.c_str(), m_ShadowCubeStem.c_str(), + m_ShadowMatrixStem.c_str(), m_ShadowControlStem.c_str(), inProgram)); + } + } + QT3DS_ASSERT(lightIdx < numShaderLights); + SShaderLightProperties &theLightProperties(shader.m_Lights[lightIdx]); + float brightness = TranslateConstantAttenuation(theLight->m_Brightness); + + // setup light data + theLightProperties.m_LightColor = + QT3DSVec4(theLight->m_DiffuseColor.getXYZ() * brightness, 1.0); + theLightProperties.m_LightData.m_specular = + QT3DSVec4(theLight->m_SpecularColor.getXYZ() * brightness, 1.0); + theLightProperties.m_LightData.m_direction = QT3DSVec4(inLightDirections[lightIdx], 1.0); + + // TODO : This does potentially mean that we can create more shadow map entries than + // we can actually use at once. + if ((theLight->m_Scope == NULL) && (theLight->m_CastShadow && inShadowMapManager)) { + SShadowMapProperties &theShadowMapProperties(shader.m_ShadowMaps[shadowMapIdx++]); + SShadowMapEntry *pEntry = inShadowMapManager->GetShadowMapEntry(lightIdx); + if (pEntry) { + // add fixed scale bias matrix + QT3DSMat44 bias(QT3DSVec4(0.5, 0.0, 0.0, 0.0), QT3DSVec4(0.0, 0.5, 0.0, 0.0), + QT3DSVec4(0.0, 0.0, 0.5, 0.0), QT3DSVec4(0.5, 0.5, 0.5, 1.0)); + + if (theLight->m_LightType != RenderLightTypes::Directional) { + theShadowMapProperties.m_ShadowCubeTexture.Set(pEntry->m_DepthCube); + theShadowMapProperties.m_ShadowmapMatrix.Set(pEntry->m_LightView); + } else { + theShadowMapProperties.m_ShadowmapTexture.Set(pEntry->m_DepthMap); + theShadowMapProperties.m_ShadowmapMatrix.Set(bias * pEntry->m_LightVP); + } + + theShadowMapProperties.m_ShadowmapSettings.Set( + QT3DSVec4(theLight->m_ShadowBias, theLight->m_ShadowFactor, + theLight->m_ShadowMapFar, 0.0f)); + } else { + // if we have a light casting shadow we should find an entry + QT3DS_ASSERT(false); + } + } + + if (theLight->m_LightType == RenderLightTypes::Point) { + theLightProperties.m_LightData.m_position = QT3DSVec4(theLight->GetGlobalPos(), 1.0); + theLightProperties.m_LightData.m_constantAttenuation = 1.0; + theLightProperties.m_LightData.m_linearAttenuation = + TranslateLinearAttenuation(theLight->m_LinearFade); + theLightProperties.m_LightData.m_quadraticAttenuation = + TranslateQuadraticAttenuation(theLight->m_ExponentialFade); + } else if (theLight->m_LightType == RenderLightTypes::Area) { + theLightProperties.m_LightData.m_position = QT3DSVec4(theLight->GetGlobalPos(), 1.0); + + QT3DSVec3 upDir = theLight->m_GlobalTransform.getUpper3x3().transform(QT3DSVec3(0, 1, 0)); + QT3DSVec3 rtDir = theLight->m_GlobalTransform.getUpper3x3().transform(QT3DSVec3(1, 0, 0)); + + theLightProperties.m_LightData.m_up = QT3DSVec4(upDir, theLight->m_AreaHeight); + theLightProperties.m_LightData.m_right = QT3DSVec4(rtDir, theLight->m_AreaWidth); + } + theLightAmbientTotal += theLight->m_AmbientColor; + } + shader.m_LightAmbientTotal = theLightAmbientTotal.getXYZ(); + } + + // Also sets the blend function on the render context. + void SetMaterialProperties(NVRenderShaderProgram &inProgram, const SDefaultMaterial &inMaterial, + const QT3DSVec2 &inCameraVec, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + NVRenderTexture2D *inDepthTexture, NVRenderTexture2D *inSSaoTexture, + SImage *inLightProbe, SImage *inLightProbe2, QT3DSF32 inProbeHorizon, + QT3DSF32 inProbeBright, QT3DSF32 inProbe2Window, QT3DSF32 inProbe2Pos, + QT3DSF32 inProbe2Fade, QT3DSF32 inProbeFOV) + { + + NVRenderContext &context(m_RenderContext.GetRenderContext()); + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(inProgram)); + shader.m_MVP.Set(inModelViewProjection); + shader.m_NormalMatrix.Set(inNormalMatrix); + shader.m_GlobalTransform.Set(inGlobalTransform); + shader.m_DepthTexture.Set(inDepthTexture); + + shader.m_AOTexture.Set(inSSaoTexture); + + qt3ds::render::SImage *theLightProbe = inLightProbe; + qt3ds::render::SImage *theLightProbe2 = inLightProbe2; + + // If the material has its own IBL Override, we should use that image instead. + if ((inMaterial.m_IblProbe) && (inMaterial.m_IblProbe->m_TextureData.m_Texture)) { + theLightProbe = inMaterial.m_IblProbe; + } + + if (theLightProbe) { + if (theLightProbe->m_TextureData.m_Texture) { + NVRenderTextureCoordOp::Enum theHorzLightProbeTilingMode = + theLightProbe->m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum theVertLightProbeTilingMode = + theLightProbe->m_VerticalTilingMode; + theLightProbe->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + const QT3DSMat44 &textureTransform = theLightProbe->m_TextureTransform; + // We separate rotational information from offset information so that just maybe the + // shader + // will attempt to push less information to the card. + const QT3DSF32 *dataPtr(textureTransform.front()); + // The third member of the offsets contains a flag indicating if the texture was + // premultiplied or not. + // We use this to mix the texture alpha. + QT3DSVec4 offsets(dataPtr[12], dataPtr[13], + theLightProbe->m_TextureData.m_TextureFlags.IsPreMultiplied() ? 1.0f + : 0.0f, + (float)theLightProbe->m_TextureData.m_Texture->GetNumMipmaps()); + + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + shader.m_LightProbeRot.Set(rotations); + shader.m_LightProbeOfs.Set(offsets); + + if ((!inMaterial.m_IblProbe) && (inProbeFOV < 180.f)) { + shader.m_LightProbeOpts.Set( + QT3DSVec4(0.01745329251994329547f * inProbeFOV, 0.0f, 0.0f, 0.0f)); + } + + // Also make sure to add the secondary texture, but it should only be added if the + // primary + // (i.e. background) texture is also there. + if (theLightProbe2 && theLightProbe2->m_TextureData.m_Texture) { + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + shader.m_LightProbe2.Set(theLightProbe2->m_TextureData.m_Texture); + shader.m_LightProbe2Props.Set( + QT3DSVec4(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f)); + + const QT3DSMat44 &xform2 = theLightProbe2->m_TextureTransform; + const QT3DSF32 *dataPtr(xform2.front()); + shader.m_LightProbeProps.Set( + QT3DSVec4(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f)); + } else { + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + shader.m_LightProbeProps.Set( + QT3DSVec4(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f)); + } + NVRenderTexture2D *textureImage = theLightProbe->m_TextureData.m_Texture; + shader.m_LightProbe.Set(textureImage); + shader.m_LightProbeSize.Set(QT3DSVec2(textureImage->GetTextureDetails().m_Width, + textureImage->GetTextureDetails().m_Height)); + } else { + shader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + } else { + shader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + QT3DSF32 emissivePower = 1.0; + + QT3DSU32 hasLighting = inMaterial.m_Lighting != DefaultMaterialLighting::NoLighting; + if (hasLighting) + emissivePower = inMaterial.m_EmissivePower / 100.0f; + + QT3DSVec4 material_diffuse = QT3DSVec4(inMaterial.m_EmissiveColor[0] * emissivePower, + inMaterial.m_EmissiveColor[1] * emissivePower, + inMaterial.m_EmissiveColor[2] * emissivePower, inOpacity); + shader.m_MaterialDiffuse.Set(material_diffuse); + shader.m_DiffuseColor.Set(inMaterial.m_DiffuseColor.getXYZ()); + QT3DSVec4 material_specular = + QT3DSVec4(inMaterial.m_SpecularTint[0], inMaterial.m_SpecularTint[1], + inMaterial.m_SpecularTint[2], inMaterial.m_IOR); + shader.m_MaterialSpecular.Set(material_specular); + shader.m_CameraProperties.Set(inCameraVec); + shader.m_FresnelPower.Set(inMaterial.m_FresnelPower); + + if (context.GetConstantBufferSupport()) { + NVRenderConstantBuffer *pLightCb = GetLightConstantBuffer(shader.m_Lights.size()); + // if we have lights we need a light buffer + QT3DS_ASSERT(shader.m_Lights.size() == 0 || pLightCb); + + for (QT3DSU32 idx = 0, end = shader.m_Lights.size(); idx < end && pLightCb; ++idx) { + shader.m_Lights[idx].m_LightData.m_diffuse = + QT3DSVec4(shader.m_Lights[idx].m_LightColor.x * inMaterial.m_DiffuseColor.x, + shader.m_Lights[idx].m_LightColor.y * inMaterial.m_DiffuseColor.y, + shader.m_Lights[idx].m_LightColor.z * inMaterial.m_DiffuseColor.z, 1.0); + + // this is our final change update memory + pLightCb->UpdateRaw(idx * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&shader.m_Lights[idx].m_LightData, + sizeof(SLightSourceShader))); + } + // update light buffer to hardware + if (pLightCb) { + QT3DSI32 cgLights = shader.m_Lights.size(); + pLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + shader.m_LightsBuffer.Set(); + } + } else { + SLightConstantProperties<SShaderGeneratorGeneratedShader> *pLightConstants + = GetLightConstantProperties(shader); + + // if we have lights we need a light buffer + QT3DS_ASSERT(shader.m_Lights.size() == 0 || pLightConstants); + + for (QT3DSU32 idx = 0, end = shader.m_Lights.size(); + idx < end && pLightConstants; ++idx) { + shader.m_Lights[idx].m_LightData.m_diffuse = + QT3DSVec4(shader.m_Lights[idx].m_LightColor.x * inMaterial.m_DiffuseColor.x, + shader.m_Lights[idx].m_LightColor.y * inMaterial.m_DiffuseColor.y, + shader.m_Lights[idx].m_LightColor.z * inMaterial.m_DiffuseColor.z, 1.0); + } + // update light buffer to hardware + if (pLightConstants) + pLightConstants->updateLights(shader); + } + + shader.m_MaterialDiffuseLightAmbientTotal.Set( + QT3DSVec3(shader.m_LightAmbientTotal.x * inMaterial.m_DiffuseColor[0], + shader.m_LightAmbientTotal.y * inMaterial.m_DiffuseColor[1], + shader.m_LightAmbientTotal.z * inMaterial.m_DiffuseColor[2])); + + shader.m_MaterialProperties.Set(QT3DSVec4( + inMaterial.m_SpecularAmount, inMaterial.m_SpecularRoughness, emissivePower, 0.0f)); + shader.m_BumpAmount.Set(inMaterial.m_BumpAmount); + shader.m_DisplaceAmount.Set(inMaterial.m_DisplaceAmount); + shader.m_TranslucentFalloff.Set(inMaterial.m_TranslucentFalloff); + shader.m_DiffuseLightWrap.Set(inMaterial.m_DiffuseLightWrap); + QT3DSU32 imageIdx = 0; + for (SRenderableImage *theImage = inFirstImage; theImage; + theImage = theImage->m_NextImage, ++imageIdx) + SetImageShaderVariables(shader, *theImage, imageIdx); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc; + qt3ds::render::NVRenderBlendEquationArgument blendEqua(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + // The blend function goes: + // src op + // dst op + // src alpha op + // dst alpha op + // All of our shaders produce non-premultiplied values. + switch (inMaterial.m_BlendMode) { + case DefaultMaterialBlendMode::Screen: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + break; + case DefaultMaterialBlendMode::Multiply: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::DstColor, NVRenderDstBlendFunc::Zero, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + break; + case DefaultMaterialBlendMode::Overlay: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Overlay, NVRenderBlendEquation::Overlay); + break; + case DefaultMaterialBlendMode::ColorBurn: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorBurn, NVRenderBlendEquation::ColorBurn); + break; + case DefaultMaterialBlendMode::ColorDodge: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorDodge, NVRenderBlendEquation::ColorDodge); + break; + default: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + break; + } + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqua); + } + void SetMaterialProperties(NVRenderShaderProgram &inProgram, + const SGraphObject &inMaterial, const QT3DSVec2 &inCameraVec, + const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, + const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override + { + const SDefaultMaterial &theMaterial(static_cast<const SDefaultMaterial &>(inMaterial)); + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::DefaultMaterial); + + SetGlobalProperties(inProgram, inRenderProperties.m_Layer, inRenderProperties.m_Camera, + inRenderProperties.m_CameraDirection, inRenderProperties.m_Lights, + inRenderProperties.m_LightDirections, + inRenderProperties.m_ShadowMapManager); + SetMaterialProperties(inProgram, theMaterial, inCameraVec, inModelViewProjection, + inNormalMatrix, inGlobalTransform, inFirstImage, inOpacity, + inRenderProperties.m_DepthTexture, inRenderProperties.m_SSaoTexture, + inRenderProperties.m_LightProbe, inRenderProperties.m_LightProbe2, + inRenderProperties.m_ProbeHorizon, inRenderProperties.m_ProbeBright, + inRenderProperties.m_Probe2Window, inRenderProperties.m_Probe2Pos, + inRenderProperties.m_Probe2Fade, inRenderProperties.m_ProbeFOV); + } + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetLightConstantProperties(SShaderGeneratorGeneratedShader &shader) + { + if (!shader.m_lightConstantProperties + || int(shader.m_Lights.size()) + > shader.m_lightConstantProperties->m_constants.size()) { + if (shader.m_lightConstantProperties) + delete shader.m_lightConstantProperties; + shader.m_lightConstantProperties + = new SLightConstantProperties<SShaderGeneratorGeneratedShader>( + shader, m_LightsAsSeparateUniforms); + } + return shader.m_lightConstantProperties; + } +}; +} + +IDefaultMaterialShaderGenerator & +IDefaultMaterialShaderGenerator::CreateDefaultMaterialShaderGenerator(IQt3DSRenderContext &inRc) +{ + return *QT3DS_NEW(inRc.GetAllocator(), SShaderGenerator)(inRc); +} diff --git a/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.h b/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.h new file mode 100644 index 0000000..7453e1d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_DEFAULT_MATERIAL_SHADER_GENERATOR_H +#define QT3DS_RENDER_DEFAULT_MATERIAL_SHADER_GENERATOR_H +#include "Qt3DSRenderMaterialShaderGenerator.h" +#include "Qt3DSRenderLightConstantProperties.h" + +namespace qt3ds { +namespace render { + + class Qt3DSShadowMap; + struct SShaderGeneratorGeneratedShader; + + class IDefaultMaterialVertexPipeline : public IShaderStageGenerator + { + protected: + virtual ~IDefaultMaterialVertexPipeline() {} + public: + // Responsible for beginning all vertex and fragment generation (void main() { etc). + virtual void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) = 0; + // The fragment shader expects a floating point constant, object_opacity to be defined + // post this method. + virtual void BeginFragmentGeneration() = 0; + // Output variables may be mangled in some circumstances so the shader generation system + // needs an abstraction + // mechanism around this. + virtual void AssignOutput(const char8_t *inVarName, const char8_t *inVarValueExpr) = 0; + + /** + * @brief Generates UV coordinates in shader code + * + * @param[in] inUVSet index of UV data set + * + * @return no return + */ + virtual void GenerateUVCoords(QT3DSU32 inUVSet = 0) = 0; + + virtual void GenerateEnvMapReflection() = 0; + virtual void GenerateViewVector() = 0; + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + virtual void GenerateWorldNormal() = 0; // world_normal in both vert and frag shader + virtual void GenerateObjectNormal() = 0; // object_normal in both vert and frag shader + virtual void + GenerateWorldPosition() = 0; // model_world_position in both vert and frag shader + virtual void GenerateVarTangentAndBinormal() = 0; + virtual void GenerateVertexColor() = 0; + + virtual bool HasActiveWireframe() = 0; // varEdgeDistance is a valid entity + + // responsible for closing all vertex and fragment generation + virtual void EndVertexGeneration(bool customShader) = 0; + virtual void EndFragmentGeneration(bool customShader) = 0; + }; + + class IDefaultMaterialShaderGenerator : public IMaterialShaderGenerator + { + public: + virtual void AddDisplacementImageUniforms(IShaderStageGenerator &inGenerator, + QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) = 0; + SImageVariableNames GetImageVariableNames(QT3DSU32 inIdx) override = 0; + void GenerateImageUVCoordinates(IShaderStageGenerator &inVertexPipeline, QT3DSU32 idx, + QT3DSU32 uvSet, SRenderableImage &image) override = 0; + // Transforms attr_pos, attr_norm, and attr_uv0. + virtual void AddDisplacementMappingForDepthPass(IShaderStageGenerator &inShader) = 0; + + // inPipelineName needs to be unique else the shader cache will just return shaders from + // different pipelines. + NVRenderShaderProgram *GenerateShader( + const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, bool inHasTransparency, + const char8_t *inVertexPipelineName, const char8_t *inCustomMaterialName = "") override = 0; + + // Also sets the blend function on the render context. + virtual void + SetMaterialProperties(NVRenderShaderProgram &inProgram, const SGraphObject &inMaterial, + const QT3DSVec2 &inCameraVec, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override = 0; + + static IDefaultMaterialShaderGenerator & + CreateDefaultMaterialShaderGenerator(IQt3DSRenderContext &inRenderContext); + + SLightConstantProperties<SShaderGeneratorGeneratedShader> + *GetLightConstantProperties(SShaderGeneratorGeneratedShader &shader); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderDynamicObjectSystem.cpp b/src/runtimerender/Qt3DSRenderDynamicObjectSystem.cpp new file mode 100644 index 0000000..92c337a --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDynamicObjectSystem.cpp @@ -0,0 +1,1532 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderDynamicObjectSystem.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderContextCore.h" +#include "render/Qt3DSRenderShaderConstant.h" +#include "Qt3DSRenderDynamicObject.h" +#include "foundation/SerializationTypes.h" +#include "foundation/FileTools.h" +#include "foundation/PreAllocatedAllocator.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StringTools.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderDynamicObjectSystemCommands.h" +#include "Qt3DSRenderDynamicObjectSystemUtil.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "foundation/Qt3DSMutex.h" + +using namespace qt3ds; +using namespace qt3ds::render; +using namespace qt3ds::render::dynamic; + +namespace { +typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair; +} + +namespace eastl { +template <> +struct hash<TStrStrPair> +{ + size_t operator()(const TStrStrPair &item) const + { + return hash<CRegisteredString>()(item.first) ^ hash<CRegisteredString>()(item.second); + } +}; +} + +namespace qt3ds { +namespace render { + namespace dynamic { + + QT3DSU32 SCommand::GetSizeofCommand(const SCommand &inCommand) + { + switch (inCommand.m_Type) { + case CommandTypes::AllocateBuffer: + return sizeof(SAllocateBuffer); + case CommandTypes::BindBuffer: + return sizeof(SBindBuffer); + case CommandTypes::BindTarget: + return sizeof(SBindTarget); + case CommandTypes::BindShader: + return sizeof(SBindShader); + case CommandTypes::Render: + return sizeof(SRender); + case CommandTypes::ApplyBufferValue: + return sizeof(SApplyBufferValue); + case CommandTypes::ApplyDepthValue: + return sizeof(SApplyDepthValue); + case CommandTypes::ApplyInstanceValue: + return sizeof(SApplyInstanceValue); + case CommandTypes::ApplyBlending: + return sizeof(SApplyBlending); + case CommandTypes::ApplyRenderState: + return sizeof(SApplyRenderState); + case CommandTypes::ApplyBlitFramebuffer: + return sizeof(SApplyBlitFramebuffer); + case CommandTypes::ApplyValue: + return sizeof(SApplyValue) + + static_cast<const SApplyValue &>(inCommand).m_Value.mSize; + case CommandTypes::DepthStencil: + return sizeof(SDepthStencil); + case CommandTypes::AllocateImage: + return sizeof(SAllocateImage); + case CommandTypes::ApplyImageValue: + return sizeof(SApplyImageValue); + case CommandTypes::AllocateDataBuffer: + return sizeof(SAllocateDataBuffer); + case CommandTypes::ApplyDataBufferValue: + return sizeof(SApplyDataBufferValue); + default: + break; + } + QT3DS_ASSERT(false); + return 0; + } + + template <typename TCommandType> + inline void CopyConstructCommandT(QT3DSU8 *inDataBuffer, const SCommand &inCommand, + IStringTable &inStrTable) + { + TCommandType *theCommand = (TCommandType *)inDataBuffer; + theCommand = new (theCommand) + TCommandType(static_cast<const TCommandType &>(inCommand), inStrTable); + } + + void SCommand::CopyConstructCommand(QT3DSU8 *inDataBuffer, const SCommand &inCommand, + IStringTable &inStrTable) + { + switch (inCommand.m_Type) { + case CommandTypes::AllocateBuffer: + CopyConstructCommandT<SAllocateBuffer>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::BindBuffer: + CopyConstructCommandT<SBindBuffer>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::BindTarget: + CopyConstructCommandT<SBindTarget>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::BindShader: + CopyConstructCommandT<SBindShader>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::Render: + CopyConstructCommandT<SRender>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyBufferValue: + CopyConstructCommandT<SApplyBufferValue>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyDepthValue: + CopyConstructCommandT<SApplyDepthValue>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyInstanceValue: + CopyConstructCommandT<SApplyInstanceValue>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyBlending: + CopyConstructCommandT<SApplyBlending>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyRenderState: + CopyConstructCommandT<SApplyRenderState>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyBlitFramebuffer: + CopyConstructCommandT<SApplyBlitFramebuffer>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyValue: { + CopyConstructCommandT<SApplyValue>(inDataBuffer, inCommand, inStrTable); + SApplyValue &dest = *reinterpret_cast<SApplyValue *>(inDataBuffer); + QT3DSU8 *destMem = inDataBuffer + sizeof(SApplyValue); + const SApplyValue &src = static_cast<const SApplyValue &>(inCommand); + memcpy(destMem, src.m_Value.mData, src.m_Value.mSize); + dest.m_Value.mData = destMem; + break; + } + case CommandTypes::DepthStencil: + CopyConstructCommandT<SDepthStencil>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::AllocateImage: + CopyConstructCommandT<SAllocateImage>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyImageValue: + CopyConstructCommandT<SApplyImageValue>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::AllocateDataBuffer: + CopyConstructCommandT<SAllocateDataBuffer>(inDataBuffer, inCommand, inStrTable); + break; + case CommandTypes::ApplyDataBufferValue: + CopyConstructCommandT<SApplyDataBufferValue>(inDataBuffer, inCommand, inStrTable); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + } +} +} + +namespace { + +template <typename TCommandType> +struct SCommandRemapping +{ + template <typename TRemapper> + static void RemapCommandData(TCommandType &, TRemapper &) + { + } +}; + +template <> +struct SCommandRemapping<SAllocateBuffer> +{ + template <typename TRemapper> + static void RemapCommandData(SAllocateBuffer &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_Name); + } +}; + +template <> +struct SCommandRemapping<SAllocateImage> +{ + template <typename TRemapper> + static void RemapCommandData(SAllocateImage &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_Name); + } +}; + +template <> +struct SCommandRemapping<SAllocateDataBuffer> +{ + template <typename TRemapper> + static void RemapCommandData(SAllocateDataBuffer &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_Name); + if (cmd.m_WrapName) + remapper.Remap(cmd.m_WrapName); + } +}; + +template <> +struct SCommandRemapping<SBindBuffer> +{ + template <typename TRemapper> + static void RemapCommandData(SBindBuffer &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_BufferName); + } +}; +template <> +struct SCommandRemapping<SBindShader> +{ + template <typename TRemapper> + static void RemapCommandData(SBindShader &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_ShaderPath); + remapper.Remap(cmd.m_ShaderDefine); + } +}; +template <> +struct SCommandRemapping<SApplyInstanceValue> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyInstanceValue &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_PropertyName); + } +}; +template <> +struct SCommandRemapping<SApplyBufferValue> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyBufferValue &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_BufferName); + remapper.Remap(cmd.m_ParamName); + } +}; + +template <> +struct SCommandRemapping<SApplyDepthValue> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyDepthValue &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_ParamName); + } +}; + +template <> +struct SCommandRemapping<SApplyBlitFramebuffer> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyBlitFramebuffer &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_SourceBufferName); + remapper.Remap(cmd.m_DestBufferName); + } +}; + +template <> +struct SCommandRemapping<SApplyValue> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyValue &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_PropertyName); + } +}; + +template <> +struct SCommandRemapping<SApplyDataBufferValue> +{ + template <typename TRemapper> + static void RemapCommandData(SApplyDataBufferValue &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_ParamName); + } +}; + +template <> +struct SCommandRemapping<SDepthStencil> +{ + template <typename TRemapper> + static void RemapCommandData(SDepthStencil &cmd, TRemapper &remapper) + { + remapper.Remap(cmd.m_BufferName); + } +}; + +QT3DSU32 Align(QT3DSU32 inValue) +{ + if (inValue % 4) + return inValue + (4 - (inValue % 4)); + return inValue; +} + +QT3DSU32 Align8(QT3DSU32 inValue) +{ + if (inValue % 8) + return inValue + (8 - (inValue % 8)); + return inValue; +} + +inline const char *GetShaderDatatypeName(NVRenderShaderDataTypes::Enum inValue) +{ + switch (inValue) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(type) \ + case NVRenderShaderDataTypes::type: \ + return #type; + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + break; + } + QT3DS_ASSERT(false); + return ""; +} + +inline qt3ds::QT3DSU32 getSizeofShaderDataType(NVRenderShaderDataTypes::Enum value) +{ + using namespace qt3ds; + using namespace qt3ds::render; + switch (value) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(x) \ + case NVRenderShaderDataTypes::x: \ + return sizeof(x); + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + break; + } + QT3DS_ASSERT(false); + return 0; +} + +struct SDynamicObjectShaderInfo +{ + CRegisteredString m_Type; ///< shader type (GLSL or HLSL) + CRegisteredString m_Version; ///< shader version (e.g. 330 vor GLSL) + bool m_HasGeomShader; + bool m_IsComputeShader; + + SDynamicObjectShaderInfo() + : m_HasGeomShader(false) + , m_IsComputeShader(false) + { + } + SDynamicObjectShaderInfo(CRegisteredString inType, CRegisteredString inVersion, + bool inHasGeomShader, bool inIsComputeShader) + : m_Type(inType) + , m_Version(inVersion) + , m_HasGeomShader(inHasGeomShader) + , m_IsComputeShader(inIsComputeShader) + { + } +}; + +struct SDynamicObjClassImpl : public IDynamicObjectClass +{ + NVAllocatorCallback *m_Allocator; + CRegisteredString m_Id; + NVConstDataRef<SPropertyDefinition> m_PropertyDefinitions; + QT3DSU32 m_PropertySectionByteSize; + QT3DSU32 m_BaseObjectSize; + GraphObjectTypes::Enum m_GraphObjectType; + QT3DSU8 *m_PropertyDefaultData; + NVConstDataRef<SCommand *> m_RenderCommands; + volatile QT3DSI32 mRefCount; + bool m_RequiresDepthTexture; + bool m_RequiresCompilation; + NVRenderTextureFormats::Enum m_OutputFormat; + + SDynamicObjClassImpl( + NVAllocatorCallback &alloc, CRegisteredString id, + NVConstDataRef<SPropertyDefinition> definitions, QT3DSU32 propertySectionByteSize, + QT3DSU32 baseObjectSize, GraphObjectTypes::Enum objectType, QT3DSU8 *propDefaultData, + bool inRequiresDepthTexture = false, + NVRenderTextureFormats::Enum inOutputFormat = NVRenderTextureFormats::RGBA8) + : m_Allocator(&alloc) + , m_Id(id) + , m_PropertyDefinitions(definitions) + , m_PropertySectionByteSize(propertySectionByteSize) + , m_BaseObjectSize(baseObjectSize) + , m_GraphObjectType(objectType) + , m_PropertyDefaultData(propDefaultData) + , mRefCount(0) + , m_RequiresDepthTexture(inRequiresDepthTexture) + , m_RequiresCompilation(false) + , m_OutputFormat(inOutputFormat) + { + } + + ~SDynamicObjClassImpl() + { + if (m_PropertyDefinitions.size()) { + for (QT3DSU32 idx = 0, end = m_PropertyDefinitions.size(); idx < end; ++idx) { + if (m_PropertyDefinitions[idx].m_EnumValueNames.size()) + m_Allocator->deallocate( + (void *)m_PropertyDefinitions[idx].m_EnumValueNames.begin()); + } + } + ReleaseCommands(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(*m_Allocator) + + template <typename TRemapperType> + static void RemapCommand(SCommand &inCommand, TRemapperType &inRemapper) + { + switch (inCommand.m_Type) { +#define QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(type) \ + case CommandTypes::type: \ + SCommandRemapping<S##type>::RemapCommandData(static_cast<S##type &>(inCommand), \ + inRemapper); \ + break; + QT3DS_RENDER_EFFECTS_ITERATE_COMMAND_TYPES +#undef QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES + default: + QT3DS_ASSERT(false); + break; + } + } + template <typename TRemapper> + void SetupThisObjectFromMemory(NVAllocatorCallback &inAlloc, TRemapper &inRemapper, + QT3DSU8 *inCommandStart, QT3DSU32 numEffectCommands) + { + m_Allocator = &inAlloc; + mRefCount = 0; + QT3DSU8 *theCommandPtrBegin = inCommandStart; + QT3DSU32 theCommandOffset = 0; + for (QT3DSU32 idx = 0; idx < numEffectCommands; ++idx) { + SCommand *theCommand = reinterpret_cast<SCommand *>(inCommandStart + theCommandOffset); + theCommandOffset += SCommand::GetSizeofCommand(*theCommand); + } + SCommand **theCommandPtrStart = + reinterpret_cast<SCommand **>(theCommandPtrBegin + theCommandOffset); + m_RenderCommands = NVConstDataRef<SCommand *>(theCommandPtrStart, numEffectCommands); + // Now run through the commands, fixup strings and setup the command ptrs + theCommandOffset = 0; + for (QT3DSU32 idx = 0; idx < numEffectCommands; ++idx) { + SCommand *theCommand = + reinterpret_cast<SCommand *>(theCommandPtrBegin + theCommandOffset); + theCommandPtrStart[idx] = theCommand; + RemapCommand(*theCommand, inRemapper); + theCommandOffset += SCommand::GetSizeofCommand(*theCommand); + } + } + + void ReleaseCommands() + { + if (m_RenderCommands.size()) { + m_Allocator->deallocate(const_cast<SCommand *>(*m_RenderCommands.begin())); + m_RenderCommands = NVConstDataRef<SCommand *>(); + } + } + + CRegisteredString GetId() const override { return m_Id; } + NVConstDataRef<SPropertyDefinition> GetProperties() const override + { + return m_PropertyDefinitions; + } + QT3DSU32 GetPropertySectionByteSize() const override { return m_PropertySectionByteSize; } + const QT3DSU8 *GetDefaultValueBuffer() const override { return m_PropertyDefaultData; } + QT3DSU32 GetBaseObjectSize() const override { return m_BaseObjectSize; } + GraphObjectTypes::Enum GraphObjectType() const override { return m_GraphObjectType; } + const SPropertyDefinition *FindDefinition(CRegisteredString &str) const + { + for (QT3DSU32 idx = 0, end = m_PropertyDefinitions.size(); idx < end; ++idx) { + const SPropertyDefinition &def(m_PropertyDefinitions[idx]); + if (def.m_Name == str) + return &def; + } + return NULL; + } + const SPropertyDefinition *FindPropertyByName(CRegisteredString inName) const override + { + return FindDefinition(inName); + } + NVConstDataRef<dynamic::SCommand *> GetRenderCommands() const override + { + return m_RenderCommands; + } + bool RequiresDepthTexture() const override { return m_RequiresDepthTexture; } + void SetRequiresDepthTexture(bool inVal) override { m_RequiresDepthTexture = inVal; } + virtual bool RequiresCompilation() const override { return m_RequiresCompilation; } + virtual void SetRequiresCompilation(bool inVal) override { m_RequiresCompilation = inVal; } + NVRenderTextureFormats::Enum GetOutputTextureFormat() const override { return m_OutputFormat; } +}; + +struct SDataRemapper +{ + template <typename TRemapper> + void Remap(QT3DSU8 *inData, SPropertyDefinition &item, TRemapper &remapper) + { + switch (item.m_DataType) { + default: + break; // no remapping necessary + case NVRenderShaderDataTypes::NVRenderTexture2DPtr: + CRegisteredString *realData = reinterpret_cast<CRegisteredString *>(inData); + remapper.Remap(*realData); + break; + } + } +}; + +struct SShaderMapKey +{ + TStrStrPair m_Name; + eastl::vector<SShaderPreprocessorFeature> m_Features; + TessModeValues::Enum m_TessMode; + bool m_WireframeMode; + size_t m_HashCode; + SShaderMapKey(TStrStrPair inName, TShaderFeatureSet inFeatures, TessModeValues::Enum inTessMode, + bool inWireframeMode) + : m_Name(inName) + , m_Features(inFeatures.begin(), inFeatures.end()) + , m_TessMode(inTessMode) + , m_WireframeMode(inWireframeMode) + { + m_HashCode = eastl::hash<TStrStrPair>()(m_Name) + ^ HashShaderFeatureSet(toDataRef(m_Features.data(), (QT3DSU32)m_Features.size())) + ^ eastl::hash<QT3DSU32>()(m_TessMode) ^ eastl::hash<bool>()(m_WireframeMode); + } + bool operator==(const SShaderMapKey &inKey) const + { + return m_Name == inKey.m_Name && m_Features == inKey.m_Features + && m_TessMode == inKey.m_TessMode && m_WireframeMode == inKey.m_WireframeMode; + } +}; +} + +namespace eastl { +template <> +struct hash<SShaderMapKey> +{ + size_t operator()(const SShaderMapKey &inKey) const { return inKey.m_HashCode; } +}; +} + +namespace { + +typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SDynamicObjClassImpl>> TStringClassMap; +typedef nvhash_map<CRegisteredString, char8_t *> TPathDataMap; +typedef nvhash_map<CRegisteredString, SDynamicObjectShaderInfo> TShaderInfoMap; +typedef nvhash_set<CRegisteredString> TPathSet; +typedef nvhash_map<SShaderMapKey, TShaderAndFlags> TShaderMap; + +struct SDynamicObjectSystemCoreImpl : public IDynamicObjectSystem +{ +}; + +struct SDynamicObjectSystemImpl : public IDynamicObjectSystem +{ + NVFoundationBase &m_Foundation; + mutable qt3ds::render::SPreAllocatedAllocator m_Allocator; + IQt3DSRenderContextCore &m_CoreContext; + IQt3DSRenderContext *m_Context; + TStringClassMap m_Classes; + TPathDataMap m_ExpandedFiles; + Qt3DSString m_ShaderKeyBuilder; + TShaderMap m_ShaderMap; + TShaderInfoMap m_ShaderInfoMap; + Qt3DSString m_IncludePath; + Qt3DSString m_IncludeSearch; + Qt3DSString m_VertShader; + Qt3DSString m_FragShader; + Qt3DSString m_GeometryShader; + QString m_shaderLibraryVersion; + QString m_shaderLibraryPlatformDirectory; + mutable Mutex m_PropertyLoadMutex; + QT3DSI32 mRefCount; + + SDynamicObjectSystemImpl(IQt3DSRenderContextCore &inCore) + : m_Foundation(inCore.GetFoundation()) + , m_Allocator(inCore.GetAllocator()) + , m_CoreContext(inCore) + , m_Context(NULL) + , m_Classes(inCore.GetAllocator(), "Classes") + , m_ExpandedFiles(inCore.GetAllocator(), "ExpandedShaderFiles") + , m_ShaderMap(inCore.GetAllocator(), "ShaderMap") + , m_ShaderInfoMap(inCore.GetAllocator(), "ShaderInfoMap") + , m_PropertyLoadMutex(inCore.GetAllocator()) + , mRefCount(0) + { + m_IncludeSearch = "#include \""; + } + + virtual ~SDynamicObjectSystemImpl() + { + for (TPathDataMap::iterator iter = m_ExpandedFiles.begin(), end = m_ExpandedFiles.end(); + iter != end; ++iter) + m_Allocator.deallocate(iter->second); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + bool IsRegistered(CRegisteredString inStr) override + { + return m_Classes.find(inStr) != m_Classes.end(); + } + + bool Register(CRegisteredString inName, + NVConstDataRef<SPropertyDeclaration> inProperties, QT3DSU32 inBaseObjectSize, + GraphObjectTypes::Enum inGraphObjectType) override + { + if (IsRegistered(inName)) { + QT3DS_ASSERT(false); + return false; + } + nvvector<SPropertyDefinition> definitions(m_Foundation.getAllocator(), + "PropertyDefinitions"); + QT3DSU32 theCurrentOffset = 0; + for (QT3DSU32 idx = 0, end = inProperties.size(); idx < end; ++idx) { + const SPropertyDeclaration &thePropDec = inProperties[idx]; + CRegisteredString thePropName( + m_CoreContext.GetStringTable().RegisterStr(thePropDec.m_Name)); + QT3DSU32 propSize = getSizeofShaderDataType(thePropDec.m_DataType); + definitions.push_back(SPropertyDefinition(thePropName, thePropDec.m_DataType, + theCurrentOffset, propSize)); + theCurrentOffset += propSize; + theCurrentOffset = Align(theCurrentOffset); + } + QT3DSU32 dataSectionSize = theCurrentOffset; + QT3DSU32 clsSize = Align(sizeof(SDynamicObjClassImpl)); + QT3DSU32 defSize = Align(sizeof(SPropertyDefinition) * inProperties.size()); + QT3DSU32 defaultSize = dataSectionSize; + QT3DSU32 allocSize = clsSize + defSize + defaultSize; + QT3DSU8 *allocData = reinterpret_cast<QT3DSU8 *>( + m_Allocator.allocate(allocSize, "SDynamicObjClassImpl", __FILE__, __LINE__)); + QT3DSU8 *defData = allocData + clsSize; + QT3DSU8 *defaultData = defData + defSize; + SPropertyDefinition *defPtr = reinterpret_cast<SPropertyDefinition *>(defData); + if (defSize) + memCopy(defPtr, definitions.data(), defSize); + if (defaultSize) + memZero(defaultData, defaultSize); + SDynamicObjClassImpl *theClass = new (allocData) + SDynamicObjClassImpl(m_Allocator, inName, toDataRef(defPtr, inProperties.size()), + dataSectionSize, inBaseObjectSize, inGraphObjectType, defaultData); + m_Classes.insert(eastl::make_pair(inName, theClass)); + return true; + } + + bool Unregister(CRegisteredString inName) override { + if (!IsRegistered(inName)) { + QT3DS_ASSERT(false); + return false; + } + TStringClassMap::iterator iter = m_Classes.find(inName); + if (iter != m_Classes.end()) + m_Classes.erase(iter); + return true; + } + + SDynamicObjClassImpl *FindClass(CRegisteredString inName) + { + TStringClassMap::iterator iter = m_Classes.find(inName); + if (iter != m_Classes.end()) + return iter->second.mPtr; + return NULL; + } + + eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *> + FindProperty(CRegisteredString inName, CRegisteredString inPropName) + { + SDynamicObjClassImpl *cls = FindClass(inName); + if (cls) { + const SPropertyDefinition *def = cls->FindDefinition(inPropName); + if (def) + return eastl::make_pair(def, cls); + } + return eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *>(NULL, NULL); + } + + void SetPropertyDefaultValue(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<QT3DSU8> inDefaultData) override + { + eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *> def = + FindProperty(inName, inPropName); + if (def.first && inDefaultData.size() >= def.first->m_ByteSize) { + memCopy(def.second->m_PropertyDefaultData + def.first->m_Offset, inDefaultData.begin(), + def.first->m_ByteSize); + } else { + QT3DS_ASSERT(false); + } + } + + void SetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) override + { + + eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *> def = + FindProperty(inName, inPropName); + SPropertyDefinition *theDefinitionPtr = const_cast<SPropertyDefinition *>(def.first); + if (theDefinitionPtr == NULL) { + QT3DS_ASSERT(false); + return; + } + if (theDefinitionPtr->m_EnumValueNames.size()) { + m_Foundation.getAllocator().deallocate( + (void *)theDefinitionPtr->m_EnumValueNames.begin()); + theDefinitionPtr->m_EnumValueNames = NVConstDataRef<CRegisteredString>(); + } + theDefinitionPtr->m_IsEnumProperty = true; + if (inNames.size()) { + CRegisteredString *theNameValues = (CRegisteredString *)m_Allocator.allocate( + inNames.size() * sizeof(CRegisteredString), "PropertyEnumNames", __FILE__, + __LINE__); + + memCopy(theNameValues, inNames.begin(), inNames.size() * sizeof(CRegisteredString)); + theDefinitionPtr->m_EnumValueNames = + NVConstDataRef<CRegisteredString>(theNameValues, inNames.size()); + } + } + + virtual NVConstDataRef<CRegisteredString> + GetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName) const override + { + eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *> def = + const_cast<SDynamicObjectSystemImpl &>(*this).FindProperty(inName, inPropName); + if (def.first) + return def.first->m_EnumValueNames; + return NVConstDataRef<CRegisteredString>(); + } + + // Called during loading which is pretty heavily multithreaded. + virtual NVConstDataRef<dynamic::SPropertyDefinition> + GetProperties(CRegisteredString inName) const override + { + Mutex::ScopedLock __locker(m_PropertyLoadMutex); + SDynamicObjClassImpl *cls = const_cast<SDynamicObjectSystemImpl &>(*this).FindClass(inName); + if (cls) + return cls->m_PropertyDefinitions; + return NVConstDataRef<dynamic::SPropertyDefinition>(); + } + + void SetPropertyTextureSettings(CRegisteredString inName, CRegisteredString inPropName, + CRegisteredString inPropPath, + NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, + NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) override + { + eastl::pair<const SPropertyDefinition *, SDynamicObjClassImpl *> def = + FindProperty(inName, inPropName); + SPropertyDefinition *theDefinitionPtr = const_cast<SPropertyDefinition *>(def.first); + if (theDefinitionPtr == NULL) { + QT3DS_ASSERT(false); + return; + } + theDefinitionPtr->m_ImagePath = inPropPath; + theDefinitionPtr->m_TexUsageType = inTexType; + theDefinitionPtr->m_CoordOp = inCoordOp; + theDefinitionPtr->m_MagFilterOp = inMagFilterOp; + theDefinitionPtr->m_MinFilterOp = inMinFilterOp; + } + + IDynamicObjectClass *GetDynamicObjectClass(CRegisteredString inName) override + { + return FindClass(inName); + } + + void SetRenderCommands(CRegisteredString inClassName, + NVConstDataRef<dynamic::SCommand *> inCommands) override + { + SDynamicObjClassImpl *theClass = + const_cast<SDynamicObjectSystemImpl &>(*this).FindClass(inClassName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + theClass->ReleaseCommands(); + QT3DSU32 commandAllocationSize = 0; + for (QT3DSU32 idx = 0, end = inCommands.size(); idx < end; ++idx) { + QT3DSU32 commandSize = Align(SCommand::GetSizeofCommand(*inCommands[idx])); + commandAllocationSize += commandSize; + } + QT3DSU32 commandPtrSize = inCommands.size() * sizeof(SCommand *); + QT3DSU32 totalAllocationSize = Align8(commandAllocationSize) + commandPtrSize; + QT3DSU8 *theCommandDataBegin = (QT3DSU8 *)m_Allocator.allocate( + totalAllocationSize, "dynamic::SCommand", __FILE__, __LINE__); + QT3DSU8 *theCurrentCommandData(theCommandDataBegin); + SCommand **theCommandPtrBegin = + reinterpret_cast<SCommand **>(theCommandDataBegin + Align8(commandAllocationSize)); + SCommand **theCurrentCommandPtr = theCommandPtrBegin; + memZero(theCommandDataBegin, totalAllocationSize); + + theClass->m_RequiresDepthTexture = false; + for (QT3DSU32 idx = 0, end = inCommands.size(); idx < end; ++idx) { + SCommand &theCommand(*inCommands[idx]); + QT3DSU32 theCommandSize = SCommand::GetSizeofCommand(theCommand); + SCommand::CopyConstructCommand(theCurrentCommandData, theCommand, + m_CoreContext.GetStringTable()); + if (theCommand.m_Type == CommandTypes::ApplyDepthValue) + theClass->m_RequiresDepthTexture = true; + if (theCommand.m_Type == CommandTypes::BindTarget) { + SBindTarget *bt = reinterpret_cast<SBindTarget *>(&theCommand); + theClass->m_OutputFormat = bt->m_OutputFormat; + } + + *theCurrentCommandPtr = reinterpret_cast<SCommand *>(theCurrentCommandData); + ++theCurrentCommandPtr; + theCurrentCommandData += Align(theCommandSize); + } + QT3DS_ASSERT(theCurrentCommandData - theCommandDataBegin == (int)commandAllocationSize); + QT3DS_ASSERT((QT3DSU8 *)theCurrentCommandPtr - theCommandDataBegin == (int)totalAllocationSize); + theClass->m_RenderCommands = + NVConstDataRef<SCommand *>(theCommandPtrBegin, inCommands.size()); + } + + virtual NVConstDataRef<dynamic::SCommand *> + GetRenderCommands(CRegisteredString inClassName) const override + { + SDynamicObjClassImpl *cls = + const_cast<SDynamicObjectSystemImpl &>(*this).FindClass(inClassName); + if (cls) + return cls->m_RenderCommands; + return NVConstDataRef<dynamic::SCommand *>(); + } + + SDynamicObject *CreateInstance(CRegisteredString inClassName, + NVAllocatorCallback &inSceneGraphAllocator) override + { + SDynamicObjClassImpl *theClass = FindClass(inClassName); + if (!theClass) { + QT3DS_ASSERT(false); + return NULL; + } + QT3DSU32 totalObjectSize = theClass->m_BaseObjectSize + theClass->m_PropertySectionByteSize; + SDynamicObject *retval = reinterpret_cast<SDynamicObject *>(inSceneGraphAllocator.allocate( + totalObjectSize, inClassName.c_str(), __FILE__, __LINE__)); + new (retval) + SDynamicObject(theClass->m_GraphObjectType, inClassName, + theClass->m_PropertySectionByteSize, theClass->m_BaseObjectSize); + memCopy(retval->GetDataSectionBegin(), theClass->m_PropertyDefaultData, + theClass->m_PropertySectionByteSize); + return retval; + } + + void SetShaderData(CRegisteredString inPath, const char8_t *inData, + const char8_t *inShaderType, const char8_t *inShaderVersion, + bool inHasGeomShader, bool inIsComputeShader) override + { + inData = inData ? inData : ""; + eastl::pair<TPathDataMap::iterator, bool> theInserter = + m_ExpandedFiles.insert(eastl::make_pair(inPath, (char8_t *)"")); + if (theInserter.second == false) { + // Delete the existing entry. + m_Allocator.deallocate(theInserter.first->second); + } + QT3DSU32 theLen = (QT3DSU32)strlen(inData) + 1; + char8_t *newData = (char8_t *)m_Allocator.allocate( + theLen, "SDynamicObjectSystem::SetShaderData", __FILE__, __LINE__); + memCopy(newData, inData, theLen); + theInserter.first->second = newData; + + // set shader type and version if available + if (inShaderType || inShaderVersion || inHasGeomShader || inIsComputeShader) { + // UdoL TODO: Add this to the load / save setction + // In addition we should merge the source code into SDynamicObjectShaderInfo as well + SDynamicObjectShaderInfo &theShaderInfo = + m_ShaderInfoMap.insert(eastl::make_pair(inPath, SDynamicObjectShaderInfo())) + .first->second; + IStringTable &theStringTable(m_CoreContext.GetStringTable()); + theShaderInfo.m_Type = theStringTable.RegisterStr(nonNull(inShaderType)); + theShaderInfo.m_Version = theStringTable.RegisterStr(nonNull(inShaderVersion)); + theShaderInfo.m_HasGeomShader = inHasGeomShader; + theShaderInfo.m_IsComputeShader = inIsComputeShader; + } + + return; + } + + CRegisteredString GetShaderCacheKey(const char8_t *inId, const char8_t *inProgramMacro, + const dynamic::SDynamicShaderProgramFlags &inFlags) + { + m_ShaderKeyBuilder.assign(inId); + if (inProgramMacro && *inProgramMacro) { + m_ShaderKeyBuilder.append("#"); + m_ShaderKeyBuilder.append(inProgramMacro); + } + if (inFlags.IsTessellationEnabled()) { + m_ShaderKeyBuilder.append("#"); + m_ShaderKeyBuilder.append(TessModeValues::toString(inFlags.m_TessMode)); + } + if (inFlags.IsGeometryShaderEnabled() && inFlags.m_WireframeMode) { + m_ShaderKeyBuilder.append("#"); + m_ShaderKeyBuilder.append(inFlags.wireframeToString(inFlags.m_WireframeMode)); + } + + return m_CoreContext.GetStringTable().RegisterStr(m_ShaderKeyBuilder.c_str()); + } + + void InsertShaderHeaderInformation(Qt3DSString &theReadBuffer, + const char8_t *inPathToEffect) override + { + DoInsertShaderHeaderInformation(theReadBuffer, inPathToEffect); + } + + void DoInsertShaderHeaderInformation(Qt3DSString &theReadBuffer, + const char8_t *inPathToEffect) + { + // Now do search and replace for the headers + for (Qt3DSString::size_type thePos = theReadBuffer.indexOf(m_IncludeSearch); + thePos != Qt3DSString::npos; + thePos = theReadBuffer.indexOf(m_IncludeSearch, thePos + 1)) { + Qt3DSString::size_type theEndQuote = + theReadBuffer.indexOf(QLatin1Char('\"'), thePos + m_IncludeSearch.size() + 1); + + // Indicates an unterminated include file. + if (theEndQuote == Qt3DSString::npos) { + qCCritical(INVALID_OPERATION, "Unterminated include in file: %s", inPathToEffect); + theReadBuffer.clear(); + break; + } + Qt3DSString::size_type theActualBegin = thePos + m_IncludeSearch.size(); + Qt3DSString::iterator theIncludeBegin = theReadBuffer.begin() + theActualBegin; + Qt3DSString::iterator theIncludeEnd = theReadBuffer.begin() + theEndQuote; + m_IncludePath.clear(); + m_IncludePath.append(theIncludeBegin, theIncludeEnd); + // If we haven't included the file yet this round + Qt3DSString theIncludeBuffer; + DoLoadShader(m_IncludePath.c_str(), theIncludeBuffer); + theReadBuffer = + theReadBuffer.replace(theReadBuffer.begin() + thePos, + theReadBuffer.begin() + theEndQuote + 1, + theIncludeBuffer); + } + } + + void DoLoadShader(const char8_t *inPathToEffect, Qt3DSString &outShaderData) + { + eastl::pair<TPathDataMap::iterator, bool> theInsert = + m_ExpandedFiles.insert(eastl::make_pair( + m_CoreContext.GetStringTable().RegisterStr(inPathToEffect), (char8_t *)"")); + + if (theInsert.second) { + + const QString defaultDir = m_Context->GetDynamicObjectSystem() + .GetShaderCodeLibraryDirectory(); + const QString platformDir = m_Context->GetDynamicObjectSystem() + .shaderCodeLibraryPlatformDirectory(); + + QString fullPath; + NVScopedRefCounted<IRefCountedInputStream> theStream; + if (!platformDir.isEmpty()) { + QTextStream stream(&fullPath); + stream << platformDir << QLatin1Char('/') << inPathToEffect; + theStream = m_CoreContext.GetInputStreamFactory() + .GetStreamForFile(fullPath.toLatin1().data()); + } + + if (theStream.mPtr == NULL) { + fullPath.clear(); + QTextStream stream(&fullPath); + stream << defaultDir << QLatin1Char('/') << inPathToEffect; + theStream = m_CoreContext.GetInputStreamFactory() + .GetStreamForFile(fullPath); + if (theStream.mPtr == NULL) { + fullPath.clear(); + QTextStream stream(&fullPath); + stream << defaultDir << QLatin1Char('/') << inPathToEffect; + theStream = m_CoreContext.GetInputStreamFactory() + .GetStreamForFile(fullPath); + } + } + if (theStream.mPtr != NULL) { + QT3DSU8 readBuf[1024]; + QT3DSU32 amountRead = 0; + do { + amountRead = theStream->Read(NVDataRef<QT3DSU8>(readBuf, 1024)); + if (amountRead) + outShaderData.append((const char8_t *)readBuf, amountRead); + } while (amountRead); + } else { + qCCritical(INVALID_OPERATION, "Failed to find include file %s", inPathToEffect); + QT3DS_ASSERT(false); + } + theInsert.first->second = (char8_t *)m_Allocator.allocate( + outShaderData.size() + 1, "SDynamicObjectSystem::DoLoadShader", __FILE__, __LINE__); + memCopy(theInsert.first->second, outShaderData.c_str(), + QT3DSU32(outShaderData.size()) + 1); + } else { + outShaderData.assign(theInsert.first->second); + } + + DoInsertShaderHeaderInformation(outShaderData, inPathToEffect); + } + + void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, const char8_t *inProjectDir) const override + { + QT3DSU32 startOffset = ioBuffer.size(); + ioBuffer.write((QT3DSU32)m_ExpandedFiles.size()); + for (TPathDataMap::const_iterator theIter = m_ExpandedFiles.begin(); + theIter != m_ExpandedFiles.end(); ++theIter) { + CRegisteredString thePath(theIter->first); + char8_t *theData = theIter->second; + theData = theData ? theData : (char8_t *)""; + QT3DSU32 theLen = (QT3DSU32)strlen(theData); + thePath.Remap(inRemapMap); + ioBuffer.write(thePath); + ioBuffer.write(theLen + 1); + ioBuffer.write(theData, theLen + 1); + ioBuffer.align(sizeof(void *)); + } + ioBuffer.write((QT3DSU32)m_Classes.size()); + SStringSaveRemapper theRemapper(m_Allocator, inRemapMap, inProjectDir, + m_CoreContext.GetStringTable()); + for (TStringClassMap::const_iterator iter = m_Classes.begin(), end = m_Classes.end(); + iter != end; ++iter) { + const SDynamicObjClassImpl *theClass = iter->second; + ioBuffer.align(4); + QT3DSU32 objOffset = ioBuffer.size(); + QT3DSU32 classOffset = objOffset - startOffset; + (void)classOffset; + QT3DSU32 defOffset = objOffset + sizeof(SDynamicObjClassImpl); + QT3DSU32 dataOffset = + defOffset + theClass->m_PropertyDefinitions.size() * sizeof(SPropertyDefinition); + QT3DSU32 dataEnd = dataOffset + theClass->m_PropertySectionByteSize; + QT3DSU32 writeAmount = dataEnd - objOffset; + ioBuffer.write((const QT3DSU8 *)theClass, writeAmount); + ioBuffer.align(4); + QT3DSU32 cmdOffset = 0; + QT3DSU8 *writeCommandStart = NULL; + if (theClass->m_RenderCommands.size()) { + // We know commands are allocated in a block. + const SCommand &firstCommand = *theClass->m_RenderCommands[0]; + const QT3DSU8 *commandStart = reinterpret_cast<const QT3DSU8 *>(&firstCommand); + const SCommand &lastCommand( + *theClass->m_RenderCommands[theClass->m_RenderCommands.size() - 1]); + const QT3DSU8 *commandEnd = reinterpret_cast<const QT3DSU8 *>(&lastCommand) + + SCommand::GetSizeofCommand(lastCommand); + cmdOffset = ioBuffer.size(); + ioBuffer.write(commandStart, (QT3DSU32)(commandEnd - commandStart)); + // Write location of the actual storage for the command ptr array. + ioBuffer.writeZeros(theClass->m_RenderCommands.size() * sizeof(SCommand **)); + } + ioBuffer.align(4); + if (cmdOffset) + writeCommandStart = ioBuffer.begin() + cmdOffset; + + SDynamicObjClassImpl *writeClass = + (SDynamicObjClassImpl *)(ioBuffer.begin() + objOffset); + writeClass->m_Id.Remap(inRemapMap); + writeClass->SetupThisObjectFromMemory(m_Allocator, theRemapper, writeCommandStart, + theClass->m_RenderCommands.size()); + for (QT3DSU32 idx = 0, end = theClass->m_PropertyDefinitions.size(); idx < end; ++idx) { + // Moved into the loop because writing the enumerations may resize the data buffer. + SPropertyDefinition *theDefinitions = + (SPropertyDefinition *)(ioBuffer.begin() + defOffset); + theDefinitions[idx].m_Name.Remap(inRemapMap); + const SPropertyDefinition &theDefinition(theClass->m_PropertyDefinitions[idx]); + if (theDefinitions[idx].m_EnumValueNames.size()) { + QT3DSU32 enumOffset = ioBuffer.size(); + ioBuffer.write(theDefinition.m_EnumValueNames.begin(), + theDefinition.m_EnumValueNames.size() + * sizeof(CRegisteredString)); + CRegisteredString *strPtr = + (CRegisteredString *)(ioBuffer.begin() + enumOffset); + for (QT3DSU32 enumIdx = 0, enumEnd = theDefinition.m_EnumValueNames.size(); + enumIdx < enumEnd; ++enumIdx) + strPtr[enumIdx].Remap(inRemapMap); + } + if (theDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + QT3DSU8 *theDataPtr = ioBuffer.begin() + dataOffset; + CRegisteredString *realData = + reinterpret_cast<CRegisteredString *>(theDataPtr + theDefinition.m_Offset); + realData->Remap(inRemapMap); + } + } + } + + // Write out meta information about the shader system + QT3DSU32 numShaderInfos = (QT3DSU32)m_ShaderInfoMap.size(); + ioBuffer.write(numShaderInfos); + for (TShaderInfoMap::const_iterator iter = m_ShaderInfoMap.begin(), + end = m_ShaderInfoMap.end(); + iter != end; ++iter) { + CRegisteredString infoName = iter->first; + infoName.Remap(inRemapMap); + ioBuffer.write(infoName); + const SDynamicObjectShaderInfo &theInfo = iter->second; + CRegisteredString infoType(theInfo.m_Type); + CRegisteredString infoVersion(theInfo.m_Version); + infoType.Remap(inRemapMap); + infoVersion.Remap(inRemapMap); + ioBuffer.write(infoType); + ioBuffer.write(infoVersion); + } + } + + void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) override + { + m_Allocator.m_PreAllocatedBlock = inData; + m_Allocator.m_OwnsMemory = false; + TStr workspaceStr(ForwardingAllocator(m_Foundation.getAllocator(), "ProjPath")); + qt3ds::render::SDataReader theReader(inData.begin(), inData.end()); + QT3DSU32 numMappedPaths = theReader.LoadRef<QT3DSU32>(); + for (QT3DSU32 idx = 0, end = numMappedPaths; idx < end; ++idx) { + CRegisteredString theStr(theReader.LoadRef<CRegisteredString>()); + QT3DSU32 theCharLen = theReader.LoadRef<QT3DSU32>(); + char8_t *thePathData = reinterpret_cast<char8_t *>(theReader.m_CurrentPtr); + theReader.m_CurrentPtr += theCharLen; + theReader.Align(); + theStr.Remap(inStrDataBlock); + m_ExpandedFiles.insert(eastl::make_pair(theStr, thePathData)); + } + SStringLoadRemapper theRemapper(m_Allocator, inStrDataBlock, inProjectDir, + m_CoreContext.GetStringTable()); + QT3DSU32 numClasses = theReader.LoadRef<QT3DSU32>(); + for (QT3DSU32 idx = 0, end = numClasses; idx < end; ++idx) { + theReader.Align(4); + size_t classOffset = static_cast<QT3DSU32>(theReader.m_CurrentPtr - inData.mData); + (void)classOffset; + SDynamicObjClassImpl *theClass = (SDynamicObjClassImpl *)theReader.m_CurrentPtr; + theClass->m_Allocator = &m_Allocator; + theReader.m_CurrentPtr += sizeof(SDynamicObjClassImpl); + SPropertyDefinition *theDefinitions = (SPropertyDefinition *)theReader.m_CurrentPtr; + theReader.m_CurrentPtr += + theClass->m_PropertyDefinitions.size() * sizeof(SPropertyDefinition); + QT3DSU8 *theDataBuffer = theReader.m_CurrentPtr; + theReader.m_CurrentPtr += theClass->m_PropertySectionByteSize; + theClass->m_Id.Remap(inStrDataBlock); + theClass->m_PropertyDefinitions = NVConstDataRef<SPropertyDefinition>( + theDefinitions, theClass->m_PropertyDefinitions.size()); + theClass->m_PropertyDefaultData = theDataBuffer; + theReader.Align(4); + QT3DSU8 *theCommandStart = theReader.m_CurrentPtr; + + QT3DSU32 numRenderCommands = theClass->m_RenderCommands.size(); + new (theClass) SDynamicObjClassImpl( + m_Allocator, theClass->m_Id, theClass->m_PropertyDefinitions, + theClass->m_PropertySectionByteSize, theClass->m_BaseObjectSize, + theClass->m_GraphObjectType, theClass->m_PropertyDefaultData, + theClass->m_RequiresDepthTexture, theClass->m_OutputFormat); + + theClass->SetupThisObjectFromMemory(m_Allocator, theRemapper, theCommandStart, + numRenderCommands); + + if (theClass->m_RenderCommands.size()) { + const SCommand &theLastCommand = + *theClass->m_RenderCommands[theClass->m_RenderCommands.size() - 1]; + const QT3DSU8 *theCommandEnd = reinterpret_cast<const QT3DSU8 *>(&theLastCommand) + + SCommand::GetSizeofCommand(theLastCommand); + theReader.m_CurrentPtr = const_cast<QT3DSU8 *>(theCommandEnd); + theReader.m_CurrentPtr += theClass->m_RenderCommands.size() * sizeof(SCommand **); + } + theReader.Align(4); + + for (QT3DSU32 defIdx = 0, defEnd = theClass->m_PropertyDefinitions.size(); defIdx < defEnd; + ++defIdx) { + SPropertyDefinition &theDef(theDefinitions[defIdx]); + theDef.m_Name.Remap(inStrDataBlock); + if (theDef.m_EnumValueNames.size()) { + CRegisteredString *theNames = (CRegisteredString *)theReader.m_CurrentPtr; + theReader.m_CurrentPtr += + theDef.m_EnumValueNames.size() * sizeof(CRegisteredString); + theDef.m_EnumValueNames = + NVDataRef<CRegisteredString>(theNames, theDef.m_EnumValueNames.size()); + for (QT3DSU32 enumIdx = 0, enumEnd = theDef.m_EnumValueNames.size(); + enumIdx < enumEnd; ++enumIdx) + theNames[enumIdx].Remap(inStrDataBlock); + } + if (theDef.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + CRegisteredString *realData = + reinterpret_cast<CRegisteredString *>(theDataBuffer + theDef.m_Offset); + realData->Remap(inStrDataBlock); + } + } + m_Classes.insert(eastl::make_pair(theClass->m_Id, theClass)); + } + QT3DSU32 theNumShaderInfos = theReader.LoadRef<QT3DSU32>(); + for (QT3DSU32 idx = 0, end = theNumShaderInfos; idx < end; ++idx) { + CRegisteredString name, type, version; + name = theReader.LoadRef<CRegisteredString>(); + type = theReader.LoadRef<CRegisteredString>(); + version = theReader.LoadRef<CRegisteredString>(); + name.Remap(inStrDataBlock); + type.Remap(inStrDataBlock); + version.Remap(inStrDataBlock); + SDynamicObjectShaderInfo &theInfo = + m_ShaderInfoMap.insert(eastl::make_pair(name, SDynamicObjectShaderInfo())) + .first->second; + theInfo.m_Type = type; + theInfo.m_Version = version; + } + } + + IDynamicObjectSystem &CreateDynamicSystem(IQt3DSRenderContext &rc) override + { + m_Context = &rc; + return *this; + } + + QStringList getParameters(const QString &str, int begin, int end) + { + const QString s = str.mid(begin, end - begin + 1); + return s.split(","); + } + + void insertSnapperDirectives(QString &str) + { + int beginIndex = 0; + // Snapper macros: + // #define SNAPPER_SAMPLER2D(propName, propNiceName, texFilter, texWrap, showUI ) \ + // uniform sampler2D propName; \ + // uniform int flag##propName; \ + // uniform vec4 propName##Info; \ + // vec4 texture2D_##propName(vec2 uv) \ + // { \ + // return GetTextureValue( propName, uv, propName##Info.z ); \ + // } + // + // #define SNAPPER_SAMPLER2DWITHDEFAULT(propName, propNiceName, texFilter, texWrap, defaultPath, showUI ) \ + // SNAPPER_SAMPLER2D( propName, propNiceName, texFilter, texWrap, showUI ) + // + // #define SNAPPER_SAMPLERCUBE(propName, propNiceName, texFilter, texWrap ) \ + // uniform samplerCube propName; \ + // uniform vec2 propName##UVRange; \ + // uniform int flag##propName; \ + // uniform vec2 propName##Size; + QString snapperSampler = QStringLiteral("SNAPPER_SAMPLER2D("); + QString snapperSamplerDefault = QStringLiteral("SNAPPER_SAMPLER2DWITHDEFAULT("); + QString snapperSamplerCube = QStringLiteral("SNAPPER_SAMPLERCUBE("); + QString endingBracket = QStringLiteral(")"); + + while ((beginIndex = str.indexOf(snapperSampler, beginIndex)) >= 0) { + int endIndex = str.indexOf(endingBracket, beginIndex); + const QStringList list = getParameters(str, beginIndex + snapperSampler.length(), + endIndex); + str.remove(beginIndex, endIndex - beginIndex + 1); + if (list.size() == 5) { + QString insertStr; + QTextStream stream(&insertStr); + stream << "uniform sampler2D " << list[0] << ";\n"; + stream << "uniform int flag" << list[0] << ";\n"; + stream << "vec4 " << list[0] << "Info;\n"; + stream << "vec4 texture2D_" << list[0] << "(vec2 uv) " + << "{ return GetTextureValue( " << list[0] << ", uv, " + << list[0] <<"Info.z ); }\n"; + str.insert(beginIndex, insertStr); + } + } + beginIndex = 0; + while ((beginIndex = str.indexOf(snapperSamplerDefault, beginIndex)) >= 0) { + int endIndex = str.indexOf(endingBracket, beginIndex); + const QStringList list = getParameters(str, beginIndex + snapperSamplerDefault.length(), + endIndex); + str.remove(beginIndex, endIndex - beginIndex + 1); + if (list.size() == 5) { + QString insertStr; + QTextStream stream(&insertStr); + stream << "uniform sampler2D " << list[0] << ";\n"; + stream << "uniform int flag" << list[0] << ";\n"; + stream << "vec4 " << list[0] << "Info;\n"; + stream << "vec4 texture2D_" << list[0] << "(vec2 uv) " + << "{ return GetTextureValue( " << list[0] << ", uv, " + << list[0] <<"Info.z ); }\n"; + str.insert(beginIndex, insertStr); + } + } + beginIndex = 0; + while ((beginIndex = str.indexOf(snapperSamplerCube, beginIndex)) >= 0) { + int endIndex = str.indexOf(endingBracket, beginIndex); + const QStringList list = getParameters(str, beginIndex + snapperSamplerCube.length(), + endIndex); + str.remove(beginIndex, endIndex - beginIndex + 1); + if (list.size() == 4) { + QString insertStr; + QTextStream stream(&insertStr); + stream << "uniform samplerCube " << list[0] << ";\n"; + stream << "uniform vec2 "<< list[0] << "UVRange;\n"; + stream << "uniform int flag" << list[0] << ";\n"; + stream << "uniform vec2 "<< list[0] << "Size;\n"; + str.insert(beginIndex, insertStr); + } + } + } + + NVRenderShaderProgram *CompileShader(CRegisteredString inId, const char8_t *inProgramSource, + const char8_t *inGeomSource, + CRegisteredString inProgramMacroName, + TShaderFeatureSet inFeatureSet, + const dynamic::SDynamicShaderProgramFlags &inFlags, + bool inForceCompilation = false) + { + m_VertShader.clear(); + m_FragShader.clear(); + m_GeometryShader.clear(); + SShaderCacheProgramFlags theFlags; + + m_VertShader.append("#define VERTEX_SHADER\n"); + m_FragShader.append("#define FRAGMENT_SHADER\n"); + + if (inProgramMacroName.IsValid()) { + m_VertShader.append("#define "); + m_VertShader.append(inProgramMacroName.c_str()); + m_VertShader.append("\n"); + + m_FragShader.append("#define "); + m_FragShader.append(inProgramMacroName.c_str()); + m_FragShader.append("\n"); + } + + if (inGeomSource && inFlags.IsGeometryShaderEnabled()) { + theFlags.SetGeometryShaderEnabled(true); + + m_GeometryShader.append("#define GEOMETRY_SHADER 1\n"); + m_GeometryShader.append(inGeomSource); + + m_VertShader.append("#define GEOMETRY_SHADER 1\n"); + } else if (inFlags.IsGeometryShaderEnabled()) { + theFlags.SetGeometryShaderEnabled(true); + m_GeometryShader.append("#define USER_GEOMETRY_SHADER 1\n"); + m_GeometryShader.append(inProgramSource); + m_VertShader.append("#define GEOMETRY_SHADER 0\n"); + m_FragShader.append("#define GEOMETRY_WIREFRAME_SHADER 0\n"); + } else { + m_VertShader.append("#define GEOMETRY_SHADER 0\n"); + m_FragShader.append("#define GEOMETRY_WIREFRAME_SHADER 0\n"); + } + + if (strstr(inProgramSource, "SNAPPER_SAMPLER")) { + QString programSource(inProgramSource); + insertSnapperDirectives(programSource); + QByteArray data = programSource.toLatin1(); + const char *source = data.constData(); + + m_VertShader.append(source); + m_FragShader.append(source); + } else { + m_VertShader.append(inProgramSource); + m_FragShader.append(inProgramSource); + } + + IShaderCache &theShaderCache = m_Context->GetShaderCache(); + + CRegisteredString theKey = m_Context->GetStringTable().RegisterStr( + GetShaderCacheKey(inId, inProgramMacroName, inFlags)); + + if (inForceCompilation) { + return theShaderCache.ForceCompileProgram(theKey, m_VertShader.c_str(), + m_FragShader.c_str(), NULL, NULL, + m_GeometryShader.c_str(), theFlags, + inFeatureSet, false); + } + return theShaderCache.CompileProgram(theKey, m_VertShader.c_str(), m_FragShader.c_str(), + NULL, NULL, m_GeometryShader.c_str(), theFlags, + inFeatureSet); + } + + // This just returns the custom material shader source without compiling + void GetShaderSource(CRegisteredString inPath, Qt3DSString &outBuffer) override + { + outBuffer.clear(); + outBuffer.append("#define FRAGMENT_SHADER\n"); + + Qt3DSString source; + DoLoadShader(inPath, outBuffer); + } + + TShaderAndFlags GetShaderProgram(CRegisteredString inPath, + CRegisteredString inProgramMacro, + TShaderFeatureSet inFeatureSet, + const SDynamicShaderProgramFlags &inFlags, + bool inForceCompilation) override + { + eastl::pair<const SShaderMapKey, TShaderAndFlags> theInserter( + SShaderMapKey(TStrStrPair(inPath, inProgramMacro), inFeatureSet, inFlags.m_TessMode, + inFlags.m_WireframeMode), + TShaderAndFlags()); + eastl::pair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert(theInserter)); + if (theInsertResult.second || inForceCompilation) { + NVRenderShaderProgram *theProgram = m_Context->GetShaderCache().GetProgram( + GetShaderCacheKey(inPath, inProgramMacro, inFlags), inFeatureSet); + SDynamicShaderProgramFlags theFlags(inFlags); + if (!theProgram || inForceCompilation) { + SDynamicObjectShaderInfo &theShaderInfo = + m_ShaderInfoMap.insert(eastl::make_pair(inPath, SDynamicObjectShaderInfo())) + .first->second; + if (theShaderInfo.m_IsComputeShader == false) { + Qt3DSString theShaderBuffer; + DoLoadShader(inPath, theShaderBuffer); + if (theShaderInfo.m_HasGeomShader) + theFlags.SetGeometryShaderEnabled(true); + theProgram = CompileShader(inPath, theShaderBuffer.c_str(), NULL, inProgramMacro, + inFeatureSet, theFlags, inForceCompilation); + } else { + Qt3DSString theShaderBuffer; + const char8_t *shaderVersionStr = "#version 430\n"; + if ((QT3DSU32)m_Context->GetRenderContext().GetRenderContextType() + == NVRenderContextValues::GLES3PLUS) + shaderVersionStr = "#version 310 es\n"; + DoLoadShader(inPath, theShaderBuffer); + theShaderBuffer.insert(0, shaderVersionStr); + const char8_t *programSource = theShaderBuffer.c_str(); + QT3DSU32 len = (QT3DSU32)strlen(nonNull(programSource)) + 1; + theProgram = + m_Context->GetRenderContext() + .CompileComputeSource(inPath.c_str(), + NVConstDataRef<QT3DSI8>((QT3DSI8 *)programSource, len)) + .mShader; + } + } + theInsertResult.first->second = TShaderAndFlags(theProgram, theFlags); + } + return theInsertResult.first->second; + } + + TShaderAndFlags GetDepthPrepassShader(CRegisteredString inPath, + CRegisteredString inPMacro, + TShaderFeatureSet inFeatureSet) override + { + SDynamicObjectShaderInfo &theShaderInfo = + m_ShaderInfoMap.insert(eastl::make_pair(inPath, SDynamicObjectShaderInfo())) + .first->second; + if (theShaderInfo.m_HasGeomShader == false) + return TShaderAndFlags(); + // else, here we go... + SDynamicShaderProgramFlags theFlags; + m_ShaderKeyBuilder.assign(inPMacro.c_str()); + m_ShaderKeyBuilder.append("depthprepass"); + + CRegisteredString theProgramMacro = + m_Context->GetStringTable().RegisterStr(m_ShaderKeyBuilder.c_str()); + + eastl::pair<const SShaderMapKey, TShaderAndFlags> theInserter( + SShaderMapKey(TStrStrPair(inPath, theProgramMacro), inFeatureSet, theFlags.m_TessMode, + theFlags.m_WireframeMode), + TShaderAndFlags()); + eastl::pair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert(theInserter)); + if (theInsertResult.second) { + NVRenderShaderProgram *theProgram = m_Context->GetShaderCache().GetProgram( + GetShaderCacheKey(inPath, theProgramMacro, theFlags), inFeatureSet); + SDynamicShaderProgramFlags flags(theFlags); + if (!theProgram) { + Qt3DSString theShaderBuffer; + DoLoadShader(inPath, theShaderBuffer); + SShaderVertexCodeGenerator vertexShader( + m_Context->GetStringTable(), m_Allocator, + m_Context->GetRenderContext().GetRenderContextType()); + SShaderFragmentCodeGenerator fragmentShader( + vertexShader, m_Allocator, + m_Context->GetRenderContext().GetRenderContextType()); + + vertexShader.AddAttribute("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + const char8_t *vertexSource = vertexShader.BuildShaderSource(); + const char8_t *fragmentSource = fragmentShader.BuildShaderSource(); + + Qt3DSString programBuffer; + programBuffer.assign("#ifdef VERTEX_SHADER\n"); + programBuffer.append(vertexSource); + programBuffer.append("\n#endif\n"); + programBuffer.append("\n#ifdef FRAGMENT_SHADER\n"); + programBuffer.append(fragmentSource); + programBuffer.append("\n#endif"); + flags.SetGeometryShaderEnabled(true); + theProgram = CompileShader(inPath, programBuffer.c_str(), theShaderBuffer.c_str(), + theProgramMacro, inFeatureSet, flags); + } + theInsertResult.first->second = TShaderAndFlags(theProgram, flags); + } + return theInsertResult.first->second; + } + + virtual void setShaderCodeLibraryPlatformDirectory(const QString &directory) override + { + m_shaderLibraryPlatformDirectory = directory; + } + + virtual QString shaderCodeLibraryPlatformDirectory() override + { + return m_shaderLibraryPlatformDirectory; + } +}; +} + +IDynamicObjectSystemCore & +IDynamicObjectSystemCore::CreateDynamicSystemCore(IQt3DSRenderContextCore &rc) +{ + return *QT3DS_NEW(rc.GetAllocator(), SDynamicObjectSystemImpl)(rc); +} diff --git a/src/runtimerender/Qt3DSRenderDynamicObjectSystem.h b/src/runtimerender/Qt3DSRenderDynamicObjectSystem.h new file mode 100644 index 0000000..cfe022e --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDynamicObjectSystem.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_DYNAMIC_OBJECT_SYSTEM_H +#define QT3DS_RENDER_DYNAMIC_OBJECT_SYSTEM_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSVec2.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderTessModeValues.h" +#include "Qt3DSRenderGraphObjectTypes.h" +#include "EASTL/utility.h" + +#include <QtCore/qstring.h> + +namespace qt3ds { +namespace render { + struct SDynamicObject; + + namespace dynamic { + + struct SCommand; + + struct SPropertyDeclaration + { + const char8_t *m_Name; + // The datatypes map directly to the obvious types *except* + // for NVRenderTexture2DPtr. This type will be interpreted as a + // CRegisteredString (they are the same binary size) + // and will be used to lookup the texture from the buffer manager. + NVRenderShaderDataTypes::Enum m_DataType; + + SPropertyDeclaration(const char8_t *inName, NVRenderShaderDataTypes::Enum inDtype) + : m_Name(inName) + , m_DataType(inDtype) + { + } + SPropertyDeclaration() + : m_Name("") + , m_DataType(NVRenderShaderDataTypes::Unknown) + { + } + }; + + struct SPropertyDefinition + { + CRegisteredString m_Name; + + //*not* relative to the presentation directory + CRegisteredString m_ImagePath; + // The datatypes map directly to the obvious types *except* + // for NVRenderTexture2DPtr. This type will be interpreted as a + // CRegisteredString and will be used to lookup the texture + // from the buffer manager. + NVRenderShaderDataTypes::Enum m_DataType; + // All offsets are relative to the beginning of the SEffect + // and are aligned to 4 byte boundaries. + QT3DSU32 m_Offset; + // Sizeof this datatype. + QT3DSU32 m_ByteSize; + NVConstDataRef<CRegisteredString> m_EnumValueNames; + + NVRenderTextureTypeValue::Enum + m_TexUsageType; ///< texture usage type like diffuse, specular, ... + // Applies to both s,t + NVRenderTextureCoordOp::Enum m_CoordOp; + // Set mag Filter + NVRenderTextureMagnifyingOp::Enum m_MagFilterOp; + // Set min Filter + NVRenderTextureMinifyingOp::Enum m_MinFilterOp; + bool m_IsEnumProperty; + SPropertyDefinition() + : m_DataType(NVRenderShaderDataTypes::Unknown) + , m_Offset(0) + , m_ByteSize(0) + , m_TexUsageType(NVRenderTextureTypeValue::Unknown) + , m_CoordOp(NVRenderTextureCoordOp::ClampToEdge) + , m_MagFilterOp(NVRenderTextureMagnifyingOp::Linear) + , m_MinFilterOp(NVRenderTextureMinifyingOp::Linear) + , m_IsEnumProperty(false) + { + } + SPropertyDefinition(CRegisteredString inName, NVRenderShaderDataTypes::Enum inType, + QT3DSU32 inOffset, QT3DSU32 inByteSize) + : m_Name(inName) + , m_DataType(inType) + , m_Offset(inOffset) + , m_ByteSize(inByteSize) + , m_TexUsageType(NVRenderTextureTypeValue::Unknown) + , m_CoordOp(NVRenderTextureCoordOp::ClampToEdge) + , m_MagFilterOp(NVRenderTextureMagnifyingOp::Linear) + , m_MinFilterOp(NVRenderTextureMinifyingOp::Linear) + , m_IsEnumProperty(false) + { + } + }; + + struct SDynamicShaderProgramFlags : public SShaderCacheProgramFlags + { + TessModeValues::Enum m_TessMode; + bool m_WireframeMode; + + SDynamicShaderProgramFlags() + : m_TessMode(TessModeValues::NoTess) + , m_WireframeMode(false) + { + } + + SDynamicShaderProgramFlags(TessModeValues::Enum inTessMode, bool inWireframeMode) + : m_TessMode(inTessMode) + , m_WireframeMode(inWireframeMode) + { + } + + static const char *wireframeToString(bool inEnable) + { + if (inEnable) + return "wireframeMode:true"; + else + return "wireframeMode:false"; + } + }; + } + + class IDynamicObjectClass + { + protected: + virtual ~IDynamicObjectClass() {} + public: + virtual CRegisteredString GetId() const = 0; + virtual NVConstDataRef<dynamic::SPropertyDefinition> GetProperties() const = 0; + virtual QT3DSU32 GetPropertySectionByteSize() const = 0; + virtual const QT3DSU8 *GetDefaultValueBuffer() const = 0; + virtual QT3DSU32 GetBaseObjectSize() const = 0; + virtual GraphObjectTypes::Enum GraphObjectType() const = 0; + virtual const dynamic::SPropertyDefinition * + FindPropertyByName(CRegisteredString inName) const = 0; + virtual NVConstDataRef<dynamic::SCommand *> GetRenderCommands() const = 0; + virtual bool RequiresDepthTexture() const = 0; + virtual void SetRequiresDepthTexture(bool inRequires) = 0; + virtual bool RequiresCompilation() const = 0; + virtual void SetRequiresCompilation(bool inRequires) = 0; + virtual NVRenderTextureFormats::Enum GetOutputTextureFormat() const = 0; + }; + + class IDynamicObjectSystemCore : public NVRefCounted + { + protected: + virtual ~IDynamicObjectSystemCore() {} + public: + virtual bool IsRegistered(CRegisteredString inStr) = 0; + + virtual bool Register(CRegisteredString inName, + NVConstDataRef<dynamic::SPropertyDeclaration> inProperties, + QT3DSU32 inBaseObjectSize, GraphObjectTypes::Enum inGraphObjectType) = 0; + + virtual bool Unregister(CRegisteredString inName) = 0; + + // Set the default value. THis is unnecessary if the default is zero as that is what it is + // assumed to be. + virtual void SetPropertyDefaultValue(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<QT3DSU8> inDefaultData) = 0; + + virtual void SetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) = 0; + + virtual NVConstDataRef<CRegisteredString> + GetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName) const = 0; + + virtual NVConstDataRef<dynamic::SPropertyDefinition> + GetProperties(CRegisteredString inName) const = 0; + + virtual void SetPropertyTextureSettings(CRegisteredString inName, + CRegisteredString inPropName, + CRegisteredString inPropPath, + NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, + NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) = 0; + + virtual IDynamicObjectClass *GetDynamicObjectClass(CRegisteredString inName) = 0; + + // The effect commands are the actual commands that run for a given effect. The tell the + // system exactly + // explicitly things like bind this shader, bind this render target, apply this property, + // run this shader + // See UICRenderEffectCommands.h for the list of commands. + // These commands are copied into the effect. + virtual void SetRenderCommands(CRegisteredString inClassName, + NVConstDataRef<dynamic::SCommand *> inCommands) = 0; + virtual NVConstDataRef<dynamic::SCommand *> + GetRenderCommands(CRegisteredString inClassName) const = 0; + + virtual SDynamicObject *CreateInstance(CRegisteredString inClassName, + NVAllocatorCallback &inSceneGraphAllocator) = 0; + + // scan shader for #includes and insert any found" + virtual void InsertShaderHeaderInformation(Qt3DSString &inShader, + const char *inLogPath) = 0; + + // Set the shader data for a given path. Used when a path doesn't correspond to a file but + // the data has been + // auto-generated. The system will look for data under this path key during the BindShader + // effect command. + virtual void SetShaderData(CRegisteredString inPath, const char8_t *inData, + const char8_t *inShaderType = NULL, + const char8_t *inShaderVersion = NULL, + bool inHasGeomShader = false, + bool inIsComputeShader = false) = 0; + + // Overall save functions for saving the class information out to the binary file. + virtual void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t *inProjectDir) const = 0; + virtual void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) = 0; + + virtual IDynamicObjectSystem &CreateDynamicSystem(IQt3DSRenderContext &rc) = 0; + + static IDynamicObjectSystemCore &CreateDynamicSystemCore(IQt3DSRenderContextCore &rc); + }; + + typedef eastl::pair<NVScopedRefCounted<NVRenderShaderProgram>, + dynamic::SDynamicShaderProgramFlags> + TShaderAndFlags; + + class IDynamicObjectSystem : public IDynamicObjectSystemCore + { + protected: + virtual ~IDynamicObjectSystem() {} + + public: + virtual TShaderAndFlags + GetShaderProgram(CRegisteredString inPath, CRegisteredString inProgramMacro, + TShaderFeatureSet inFeatureSet, + const dynamic::SDynamicShaderProgramFlags &inFlags, + bool inForceCompilation = false) = 0; + + virtual void GetShaderSource(CRegisteredString inPath, Qt3DSString &outSource) = 0; + + // Will return null in the case where a custom prepass shader isn't needed for this object + // If no geom shader, then no depth prepass shader. + virtual TShaderAndFlags GetDepthPrepassShader(CRegisteredString inPath, + CRegisteredString inProgramMacro, + TShaderFeatureSet inFeatureSet) = 0; + + virtual void setShaderCodeLibraryPlatformDirectory(const QString &directory) = 0; + virtual QString shaderCodeLibraryPlatformDirectory() = 0; + + static QString GetShaderCodeLibraryDirectory() { return QStringLiteral("res/effectlib"); } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderDynamicObjectSystemCommands.h b/src/runtimerender/Qt3DSRenderDynamicObjectSystemCommands.h new file mode 100644 index 0000000..857dfa9 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDynamicObjectSystemCommands.h @@ -0,0 +1,622 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_EFFECT_SYSTEM_COMMANDS_H +#define QT3DS_RENDER_EFFECT_SYSTEM_COMMANDS_H +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSFlags.h" + +namespace qt3ds { +namespace render { + namespace dynamic { + using qt3ds::render::NVRenderBufferBarrierFlags; + + struct CommandTypes + { + enum Enum { + Unknown = 0, + AllocateBuffer, + BindTarget, + BindBuffer, + BindShader, + ApplyInstanceValue, + ApplyBufferValue, + // Apply the depth buffer as an input texture. + ApplyDepthValue, + Render, // Render to current FBO + ApplyBlending, + ApplyRenderState, // apply a render state + ApplyBlitFramebuffer, + ApplyValue, + DepthStencil, + AllocateImage, + ApplyImageValue, + AllocateDataBuffer, + ApplyDataBufferValue, + }; + }; + +#define QT3DS_RENDER_EFFECTS_ITERATE_COMMAND_TYPES \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(AllocateBuffer) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(BindTarget) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(BindBuffer) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(BindShader) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyInstanceValue) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyBufferValue) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyDepthValue) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(Render) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyBlending) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyRenderState) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyBlitFramebuffer) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyValue) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(DepthStencil) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(AllocateImage) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyImageValue) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(AllocateDataBuffer) \ + QT3DS_RENDER_EFFECTS_HANDLE_COMMAND_TYPES(ApplyDataBufferValue) + + // All commands need at least two constructors. One for when they are created that should + // setup all their member variables and one for when we are copying commands from an outside + // entity into the effect system. We have to re-register strings in that case because we + // can't assume the outside entity was using the same string table we are... + struct SCommand + { + CommandTypes::Enum m_Type; + SCommand(CommandTypes::Enum inType) + : m_Type(inType) + { + } + SCommand() + : m_Type(CommandTypes::Unknown) + { + } + // Implemented in UICRenderEffectSystem.cpp + static QT3DSU32 GetSizeofCommand(const SCommand &inCommand); + static void CopyConstructCommand(QT3DSU8 *inDataBuffer, const SCommand &inCommand, + IStringTable &inStrTable); + }; + + struct AllocateBufferFlagValues + { + enum Enum { + SceneLifetime = 1, + }; + }; + + struct SAllocateBufferFlags : public NVFlags<AllocateBufferFlagValues::Enum, QT3DSU32> + { + SAllocateBufferFlags(QT3DSU32 inValues) + : NVFlags<AllocateBufferFlagValues::Enum, QT3DSU32>(inValues) + { + } + SAllocateBufferFlags() {} + void SetSceneLifetime(bool inValue) + { + clearOrSet(inValue, AllocateBufferFlagValues::SceneLifetime); + } + // If isSceneLifetime is unset the buffer is assumed to be frame lifetime and will be + // released after this render operation. + bool IsSceneLifetime() const + { + return this->operator&(AllocateBufferFlagValues::SceneLifetime); + } + }; + + struct SAllocateBuffer : public SCommand + { + CRegisteredString m_Name; + NVRenderTextureFormats::Enum m_Format; + NVRenderTextureMagnifyingOp::Enum m_FilterOp; + NVRenderTextureCoordOp::Enum m_TexCoordOp; + QT3DSF32 m_SizeMultiplier; + SAllocateBufferFlags m_BufferFlags; + SAllocateBuffer() + : SCommand(CommandTypes::AllocateBuffer) + , m_Format(NVRenderTextureFormats::RGBA8) + , m_FilterOp(NVRenderTextureMagnifyingOp::Linear) + , m_TexCoordOp(NVRenderTextureCoordOp::ClampToEdge) + , m_SizeMultiplier(1.0f) + { + } + SAllocateBuffer(CRegisteredString inName, NVRenderTextureFormats::Enum inFormat, + NVRenderTextureMagnifyingOp::Enum inFilterOp, + NVRenderTextureCoordOp::Enum inCoordOp, QT3DSF32 inMultiplier, + SAllocateBufferFlags inFlags) + : SCommand(CommandTypes::AllocateBuffer) + , m_Name(inName) + , m_Format(inFormat) + , m_FilterOp(inFilterOp) + , m_TexCoordOp(inCoordOp) + , m_SizeMultiplier(inMultiplier) + , m_BufferFlags(inFlags) + { + } + SAllocateBuffer(const SAllocateBuffer &inOther, IStringTable &inStrTable) + : SCommand(CommandTypes::AllocateBuffer) + , m_Name(inStrTable.RegisterStr(inOther.m_Name)) + , m_Format(inOther.m_Format) + , m_FilterOp(inOther.m_FilterOp) + , m_TexCoordOp(inOther.m_TexCoordOp) + , m_SizeMultiplier(inOther.m_SizeMultiplier) + , m_BufferFlags(inOther.m_BufferFlags) + { + } + }; + + struct SAllocateImage : public SAllocateBuffer + { + NVRenderImageAccessType::Enum m_Access; + + SAllocateImage() + : SAllocateBuffer() + , m_Access(NVRenderImageAccessType::ReadWrite) + { + m_Type = CommandTypes::AllocateImage; + } + SAllocateImage(CRegisteredString inName, NVRenderTextureFormats::Enum inFormat, + NVRenderTextureMagnifyingOp::Enum inFilterOp, + NVRenderTextureCoordOp::Enum inCoordOp, QT3DSF32 inMultiplier, + SAllocateBufferFlags inFlags, NVRenderImageAccessType::Enum inAccess) + : SAllocateBuffer(inName, inFormat, inFilterOp, inCoordOp, inMultiplier, inFlags) + , m_Access(inAccess) + { + m_Type = CommandTypes::AllocateImage; + } + + SAllocateImage(const SAllocateImage &inOther, IStringTable &inStrTable) + : SAllocateBuffer(inStrTable.RegisterStr(inOther.m_Name), inOther.m_Format, + inOther.m_FilterOp, inOther.m_TexCoordOp, + inOther.m_SizeMultiplier, inOther.m_BufferFlags) + , m_Access(inOther.m_Access) + { + m_Type = CommandTypes::AllocateImage; + } + }; + + struct SAllocateDataBuffer : public SCommand + { + CRegisteredString m_Name; + NVRenderBufferBindValues::Enum m_DataBufferType; + CRegisteredString m_WrapName; + NVRenderBufferBindValues::Enum m_DataBufferWrapType; + QT3DSF32 m_Size; + SAllocateBufferFlags m_BufferFlags; + + SAllocateDataBuffer() + : SCommand(CommandTypes::AllocateDataBuffer) + { + } + + SAllocateDataBuffer(CRegisteredString inName, + NVRenderBufferBindValues::Enum inBufferType, + CRegisteredString inWrapName, + NVRenderBufferBindValues::Enum inBufferWrapType, QT3DSF32 inSize, + SAllocateBufferFlags inFlags) + : SCommand(CommandTypes::AllocateDataBuffer) + , m_Name(inName) + , m_DataBufferType(inBufferType) + , m_WrapName(inWrapName) + , m_DataBufferWrapType(inBufferWrapType) + , m_Size(inSize) + , m_BufferFlags(inFlags) + { + } + + SAllocateDataBuffer(const SAllocateDataBuffer &inOther, IStringTable &inStrTable) + : SCommand(CommandTypes::AllocateDataBuffer) + , m_Name(inStrTable.RegisterStr(inOther.m_Name)) + , m_DataBufferType(inOther.m_DataBufferType) + , m_WrapName(inStrTable.RegisterStr(inOther.m_WrapName)) + , m_DataBufferWrapType(inOther.m_DataBufferWrapType) + , m_Size(inOther.m_Size) + , m_BufferFlags(inOther.m_BufferFlags) + { + } + }; + + struct SBindTarget : public SCommand + { + NVRenderTextureFormats::Enum m_OutputFormat; + + SBindTarget(NVRenderTextureFormats::Enum inFormat = NVRenderTextureFormats::RGBA8) + : SCommand(CommandTypes::BindTarget) + , m_OutputFormat(inFormat) + { + } + SBindTarget(const SBindTarget &inOther, IStringTable &) + : SCommand(CommandTypes::BindTarget) + , m_OutputFormat(inOther.m_OutputFormat) + { + } + }; + + struct SBindBuffer : public SCommand + { + CRegisteredString m_BufferName; + bool m_NeedsClear; + SBindBuffer(CRegisteredString inBufName, bool inNeedsClear) + : SCommand(CommandTypes::BindBuffer) + , m_BufferName(inBufName) + , m_NeedsClear(inNeedsClear) + { + } + SBindBuffer(const SBindBuffer &inOther, IStringTable &inTable) + : SCommand(CommandTypes::BindBuffer) + , m_BufferName(inTable.RegisterStr(inOther.m_BufferName)) + , m_NeedsClear(inOther.m_NeedsClear) + { + } + }; + + struct SBindShader : public SCommand + { + CRegisteredString m_ShaderPath; + // One GLSL file can hold multiple shaders in the case of multipass effects. + // This makes it significantly easier for authors to reason about the shader + // but it means we need to #define a preprocessor token to indicate which + // effect we intend to compile at this point. + CRegisteredString m_ShaderDefine; + SBindShader(CRegisteredString inShaderPath, + CRegisteredString inShaderDefine = CRegisteredString()) + : SCommand(CommandTypes::BindShader) + , m_ShaderPath(inShaderPath) + , m_ShaderDefine(inShaderDefine) + { + } + SBindShader() + : SCommand(CommandTypes::BindShader) + { + } + SBindShader(const SBindShader &inOther, IStringTable &inTable) + : SCommand(CommandTypes::BindShader) + , m_ShaderPath(inTable.RegisterStr(inOther.m_ShaderPath)) + , m_ShaderDefine(inTable.RegisterStr(inOther.m_ShaderDefine)) + { + } + }; + + // The value sits immediately after the 'this' object + // in memory. + // If propertyName is not valid then we attempt to apply all of the effect property values + // to the shader, ignoring ones that don't match up. + struct SApplyInstanceValue : public SCommand + { + // Name of value to apply in shader + CRegisteredString m_PropertyName; + // type of value + NVRenderShaderDataTypes::Enum m_ValueType; + // offset in the effect data section of value. + QT3DSU32 m_ValueOffset; + SApplyInstanceValue(CRegisteredString inName, NVRenderShaderDataTypes::Enum inValueType, + QT3DSU32 inValueOffset) + : SCommand(CommandTypes::ApplyInstanceValue) + , m_PropertyName(inName) + , m_ValueType(inValueType) + , m_ValueOffset(inValueOffset) + { + } + // Default will attempt to apply all effect values to the currently bound shader + SApplyInstanceValue() + : SCommand(CommandTypes::ApplyInstanceValue) + , m_ValueType(NVRenderShaderDataTypes::Unknown) + , m_ValueOffset(0) + { + } + SApplyInstanceValue(const SApplyInstanceValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyInstanceValue) + , m_PropertyName(inTable.RegisterStr(inOther.m_PropertyName)) + , m_ValueType(inOther.m_ValueType) + , m_ValueOffset(inOther.m_ValueOffset) + { + } + }; + + struct SApplyValue : public SCommand + { + CRegisteredString m_PropertyName; + NVRenderShaderDataTypes::Enum m_ValueType; + NVDataRef<QT3DSU8> m_Value; + SApplyValue(CRegisteredString inName, NVRenderShaderDataTypes::Enum inValueType) + : SCommand(CommandTypes::ApplyValue) + , m_PropertyName(inName) + , m_ValueType(inValueType) + { + } + // Default will attempt to apply all effect values to the currently bound shader + SApplyValue() + : SCommand(CommandTypes::ApplyValue) + , m_ValueType(NVRenderShaderDataTypes::Unknown) + { + } + + SApplyValue(const SApplyValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyValue) + , m_PropertyName(inTable.RegisterStr(inOther.m_PropertyName)) + , m_ValueType(inOther.m_ValueType) + , m_Value(inOther.m_Value) + { + } + }; + + // bind a buffer to a given shader parameter. + struct SApplyBufferValue : public SCommand + { + // If no buffer name is given then the special buffer [source] + // is assumed. + CRegisteredString m_BufferName; + // If no param name is given, the buffer is bound to the + // input texture parameter (texture0). + CRegisteredString m_ParamName; + + SApplyBufferValue(CRegisteredString bufferName, CRegisteredString shaderParam) + : SCommand(CommandTypes::ApplyBufferValue) + , m_BufferName(bufferName) + , m_ParamName(shaderParam) + { + } + SApplyBufferValue(const SApplyBufferValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyBufferValue) + , m_BufferName(inTable.RegisterStr(inOther.m_BufferName)) + , m_ParamName(inTable.RegisterStr(inOther.m_ParamName)) + { + } + }; + + // bind a buffer to a given shader parameter. + struct SApplyImageValue : public SCommand + { + CRegisteredString m_ImageName; ///< name which the image was allocated + CRegisteredString m_ParamName; ///< must match the name in the shader + bool m_BindAsTexture; ///< bind image as texture + bool m_NeedSync; ///< if true we add a memory barrier before usage + + SApplyImageValue(CRegisteredString bufferName, CRegisteredString shaderParam, + bool inBindAsTexture, bool inNeedSync) + : SCommand(CommandTypes::ApplyImageValue) + , m_ImageName(bufferName) + , m_ParamName(shaderParam) + , m_BindAsTexture(inBindAsTexture) + , m_NeedSync(inNeedSync) + { + } + SApplyImageValue(const SApplyImageValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyImageValue) + , m_ImageName(inTable.RegisterStr(inOther.m_ImageName)) + , m_ParamName(inTable.RegisterStr(inOther.m_ParamName)) + , m_BindAsTexture(inOther.m_BindAsTexture) + , m_NeedSync(inOther.m_NeedSync) + { + } + }; + + // bind a buffer to a given shader parameter. + struct SApplyDataBufferValue : public SCommand + { + CRegisteredString m_ParamName; ///< must match the name in the shader + NVRenderBufferBindValues::Enum m_BindAs; ///< to which target we bind this buffer + + SApplyDataBufferValue(CRegisteredString inShaderParam, + NVRenderBufferBindValues::Enum inBufferType) + : SCommand(CommandTypes::ApplyDataBufferValue) + , m_ParamName(inShaderParam) + , m_BindAs(inBufferType) + { + } + SApplyDataBufferValue(const SApplyDataBufferValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyDataBufferValue) + , m_ParamName(inTable.RegisterStr(inOther.m_ParamName)) + , m_BindAs(inOther.m_BindAs) + { + } + }; + + struct SApplyDepthValue : public SCommand + { + // If no param name is given, the buffer is bound to the + // input texture parameter (texture0). + CRegisteredString m_ParamName; + SApplyDepthValue(CRegisteredString param) + : SCommand(CommandTypes::ApplyDepthValue) + , m_ParamName(param) + { + } + SApplyDepthValue(const SApplyDepthValue &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyDepthValue) + , m_ParamName(inTable.RegisterStr(inOther.m_ParamName)) + { + } + }; + + struct SRender : public SCommand + { + bool m_DrawIndirect; + SRender(bool inDrawIndirect) + : SCommand(CommandTypes::Render) + , m_DrawIndirect(inDrawIndirect) + { + } + + SRender(const SRender &inOther, IStringTable &) + : SCommand(CommandTypes::Render) + , m_DrawIndirect(inOther.m_DrawIndirect) + { + } + }; + + struct SApplyBlending : public SCommand + { + NVRenderSrcBlendFunc::Enum m_SrcBlendFunc; + NVRenderDstBlendFunc::Enum m_DstBlendFunc; + + SApplyBlending(NVRenderSrcBlendFunc::Enum inSrcBlendFunc, + NVRenderDstBlendFunc::Enum inDstBlendFunc) + : SCommand(CommandTypes::ApplyBlending) + , m_SrcBlendFunc(inSrcBlendFunc) + , m_DstBlendFunc(inDstBlendFunc) + { + } + + SApplyBlending(const SApplyBlending &inOther, IStringTable &) + : SCommand(CommandTypes::ApplyBlending) + , m_SrcBlendFunc(inOther.m_SrcBlendFunc) + , m_DstBlendFunc(inOther.m_DstBlendFunc) + { + } + }; + + struct SApplyRenderState : public SCommand + { + NVRenderState::Enum m_RenderState; + bool m_Enabled; + + SApplyRenderState(qt3ds::render::NVRenderState::Enum inRenderStateValue, bool inEnabled) + : SCommand(CommandTypes::ApplyRenderState) + , m_RenderState(inRenderStateValue) + , m_Enabled(inEnabled) + { + } + + SApplyRenderState(const SApplyRenderState &inOther, IStringTable &) + : SCommand(CommandTypes::ApplyRenderState) + , m_RenderState(inOther.m_RenderState) + , m_Enabled(inOther.m_Enabled) + { + } + }; + + struct SApplyBlitFramebuffer : public SCommand + { + // If no buffer name is given then the special buffer [source] + // is assumed. Which is the default render target + CRegisteredString m_SourceBufferName; + // If no buffer name is given then the special buffer [dest] + // is assumed. Which is the default render target + CRegisteredString m_DestBufferName; + + SApplyBlitFramebuffer(CRegisteredString inSourceBufferName, + CRegisteredString inDestBufferName) + : SCommand(CommandTypes::ApplyBlitFramebuffer) + , m_SourceBufferName(inSourceBufferName) + , m_DestBufferName(inDestBufferName) + { + } + + SApplyBlitFramebuffer(const SApplyBlitFramebuffer &inOther, IStringTable &inTable) + : SCommand(CommandTypes::ApplyBlitFramebuffer) + , m_SourceBufferName(inTable.RegisterStr(inOther.m_SourceBufferName)) + , m_DestBufferName(inTable.RegisterStr(inOther.m_DestBufferName)) + { + } + }; + + struct DepthStencilFlagValues + { + enum Enum { + NoFlagValue = 0, + ClearStencil = 1 << 0, + ClearDepth = 1 << 1, + }; + }; + + struct SDepthStencilFlags : public NVFlags<DepthStencilFlagValues::Enum> + { + bool HasClearStencil() const { return operator&(DepthStencilFlagValues::ClearStencil); } + void SetClearStencil(bool value) + { + clearOrSet(value, DepthStencilFlagValues::ClearStencil); + } + + bool HasClearDepth() const { return operator&(DepthStencilFlagValues::ClearDepth); } + void SetClearDepth(bool value) + { + clearOrSet(value, DepthStencilFlagValues::ClearDepth); + } + }; + + struct SDepthStencil : public SCommand + { + CRegisteredString m_BufferName; + SDepthStencilFlags m_Flags; + qt3ds::render::NVRenderStencilOp::Enum m_StencilFailOperation; + qt3ds::render::NVRenderStencilOp::Enum m_DepthPassOperation; + qt3ds::render::NVRenderStencilOp::Enum m_DepthFailOperation; + qt3ds::render::NVRenderBoolOp::Enum m_StencilFunction; + QT3DSU32 m_Reference; + QT3DSU32 m_Mask; + + SDepthStencil() + : SCommand(CommandTypes::DepthStencil) + , m_StencilFailOperation(qt3ds::render::NVRenderStencilOp::Keep) + , m_DepthPassOperation(qt3ds::render::NVRenderStencilOp::Keep) + , m_DepthFailOperation(qt3ds::render::NVRenderStencilOp::Keep) + , m_StencilFunction(qt3ds::render::NVRenderBoolOp::Equal) + , m_Reference(0) + , m_Mask(QT3DS_MAX_U32) + { + } + + SDepthStencil(CRegisteredString bufName, SDepthStencilFlags flags, + qt3ds::render::NVRenderStencilOp::Enum inStencilOp, + qt3ds::render::NVRenderStencilOp::Enum inDepthPassOp, + qt3ds::render::NVRenderStencilOp::Enum inDepthFailOp, + qt3ds::render::NVRenderBoolOp::Enum inStencilFunc, QT3DSU32 value, QT3DSU32 mask) + : SCommand(CommandTypes::DepthStencil) + , m_BufferName(bufName) + , m_Flags(flags) + , m_StencilFailOperation(inStencilOp) + , m_DepthPassOperation(inDepthPassOp) + , m_DepthFailOperation(inDepthFailOp) + , m_StencilFunction(inStencilFunc) + , m_Reference(value) + , m_Mask(mask) + { + } + + SDepthStencil(const SDepthStencil &inOther, IStringTable &inTable) + : SCommand(CommandTypes::DepthStencil) + , m_BufferName(inTable.RegisterStr(inOther.m_BufferName)) + , m_Flags(inOther.m_Flags) + , m_StencilFailOperation(inOther.m_StencilFailOperation) + , m_DepthPassOperation(inOther.m_DepthPassOperation) + , m_DepthFailOperation(inOther.m_DepthFailOperation) + , m_StencilFunction(inOther.m_StencilFunction) + , m_Reference(inOther.m_Reference) + , m_Mask(inOther.m_Mask) + { + } + }; + } +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderDynamicObjectSystemUtil.h b/src/runtimerender/Qt3DSRenderDynamicObjectSystemUtil.h new file mode 100644 index 0000000..b00b996 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderDynamicObjectSystemUtil.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_DYNAMIC_OBJECT_SYSTEM_UTIL_H +#define QT3DS_RENDER_DYNAMIC_OBJECT_SYSTEM_UTIL_H +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "StringTools.h" + +namespace qt3ds { +namespace render { + namespace dynamic { + + struct SStringLoadRemapper + { + CStrTableOrDataRef m_StrData; + IStringTable &m_StringTable; + Qt3DSString m_PathMapper; + const char8_t *m_ProjectDir; + SStringLoadRemapper(NVAllocatorCallback &alloc, CStrTableOrDataRef inData, + const char8_t *inProjectDir, IStringTable &inStrTable) + : m_StrData(inData) + , m_StringTable(inStrTable) + , m_ProjectDir(inProjectDir) + { + Q_UNUSED(alloc) + } + void Remap(CRegisteredString &inStr) { inStr.Remap(m_StrData); } + }; + + struct SStringSaveRemapper + { + const qt3ds::render::SStrRemapMap &m_Map; + Qt3DSString m_RelativeBuffer; + Qt3DSString m_ProjectDir; + Qt3DSString m_FinalBuffer; + IStringTable &m_StringTable; + SStringSaveRemapper(NVAllocatorCallback &alloc, const qt3ds::render::SStrRemapMap &map, + const char8_t *inProjectDir, IStringTable &inStrTable) + : m_Map(map) + , m_StringTable(inStrTable) + { + Q_UNUSED(alloc) + m_ProjectDir.assign(inProjectDir); + } + void Remap(CRegisteredString &inStr) { inStr.Remap(m_Map); } + }; + + inline QT3DSU32 Align(QT3DSU32 inValue) + { + if (inValue % 4) + return inValue + (4 - (inValue % 4)); + return inValue; + } + + inline QT3DSU32 Align8(QT3DSU32 inValue) + { + if (inValue % 8) + return inValue + (8 - (inValue % 8)); + return inValue; + } + + inline qt3ds::QT3DSU32 getSizeofShaderDataType(NVRenderShaderDataTypes::Enum value) + { + using namespace qt3ds; + using namespace qt3ds::render; + switch (value) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(x) \ + case NVRenderShaderDataTypes::x: \ + return sizeof(x); + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + break; + } + QT3DS_ASSERT(false); + return 0; + } + + inline const char *GetShaderDatatypeName(NVRenderShaderDataTypes::Enum inValue) + { + switch (inValue) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(type) \ + case NVRenderShaderDataTypes::type: \ + return #type; + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + break; + } + QT3DS_ASSERT(false); + return ""; + } + } +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderEffectSystem.cpp b/src/runtimerender/Qt3DSRenderEffectSystem.cpp new file mode 100644 index 0000000..65f52a6 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderEffectSystem.cpp @@ -0,0 +1,1872 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderEffectSystem.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSContainers.h" +#include "StringTools.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSRenderResourceManager.h" +#include "Qt3DSRenderDynamicObjectSystemCommands.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderShaderConstant.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "foundation/PreAllocatedAllocator.h" +#include "foundation/SerializationTypes.h" +#include "Qt3DSRenderShaderCache.h" +#include "foundation/FileTools.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSRenderDynamicObjectSystemUtil.h" + +using namespace qt3ds::render; +using namespace qt3ds::render::dynamic; +using qt3ds::render::NVRenderContextScopedProperty; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +// None of this code will work if the size of void* changes because that would mean that +// the alignment of some of the objects isn't 4 bytes but would be 8 bytes. + +typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair; + +namespace eastl { +template <> +struct hash<TStrStrPair> +{ + size_t operator()(const TStrStrPair &item) const + { + return hash<CRegisteredString>()(item.first) ^ hash<CRegisteredString>()(item.second); + } +}; +} + +namespace { + +/* + ApplyBufferValue, + //Apply the depth buffer as an input texture. + ApplyDepthValue, + Render, //Render to current FBO + */ + +struct SEffectClass +{ + NVAllocatorCallback *m_Allocator; + IDynamicObjectClass *m_DynamicClass; + volatile QT3DSI32 mRefCount; + + SEffectClass(NVAllocatorCallback &inFnd, IDynamicObjectClass &dynClass) + : m_Allocator(&inFnd) + , m_DynamicClass(&dynClass) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(*m_Allocator) + + void SetupThisObjectFromMemory(NVAllocatorCallback &inAlloc, IDynamicObjectClass &inClass) + { + m_Allocator = &inAlloc; + m_DynamicClass = &inClass; + mRefCount = 0; + } +}; + +struct SAllocatedBufferEntry +{ + CRegisteredString m_Name; + NVScopedRefCounted<NVRenderFrameBuffer> m_FrameBuffer; + NVScopedRefCounted<NVRenderTexture2D> m_Texture; + SAllocateBufferFlags m_Flags; + bool m_NeedsClear; + + SAllocatedBufferEntry(CRegisteredString inName, NVRenderFrameBuffer &inFb, + NVRenderTexture2D &inTexture, SAllocateBufferFlags inFlags) + : m_Name(inName) + , m_FrameBuffer(&inFb) + , m_Texture(&inTexture) + , m_Flags(inFlags) + , m_NeedsClear(true) + { + } + SAllocatedBufferEntry() {} +}; + +struct SAllocatedImageEntry +{ + CRegisteredString m_Name; + NVScopedRefCounted<NVRenderImage2D> m_Image; + NVScopedRefCounted<NVRenderTexture2D> m_Texture; + SAllocateBufferFlags m_Flags; + + SAllocatedImageEntry(CRegisteredString inName, NVRenderImage2D &inImage, + NVRenderTexture2D &inTexture, SAllocateBufferFlags inFlags) + : m_Name(inName) + , m_Image(&inImage) + , m_Texture(&inTexture) + , m_Flags(inFlags) + { + } + SAllocatedImageEntry() {} +}; + +struct SImageEntry +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<NVRenderImage2D *> m_Image; + volatile QT3DSI32 mRefCount; + + SImageEntry(NVRenderShaderProgram &inShader, const char *inImageName) + : m_Shader(inShader) + , m_Image(inImageName, inShader) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + void Set(NVRenderImage2D *inImage) { m_Image.Set(inImage); } + + static SImageEntry CreateImageEntry(NVRenderShaderProgram &inShader, const char *inStem) + { + return SImageEntry(inShader, inStem); + } +}; + +struct SAllocatedDataBufferEntry +{ + CRegisteredString m_Name; + NVScopedRefCounted<qt3ds::render::NVRenderDataBuffer> m_DataBuffer; + NVRenderBufferBindValues::Enum m_BufferType; + NVDataRef<QT3DSU8> m_BufferData; + SAllocateBufferFlags m_Flags; + bool m_NeedsClear; + + SAllocatedDataBufferEntry(CRegisteredString inName, + qt3ds::render::NVRenderDataBuffer &inDataBuffer, + NVRenderBufferBindValues::Enum inType, NVDataRef<QT3DSU8> data, + SAllocateBufferFlags inFlags) + : m_Name(inName) + , m_DataBuffer(&inDataBuffer) + , m_BufferType(inType) + , m_BufferData(data) + , m_Flags(inFlags) + , m_NeedsClear(false) + { + } + SAllocatedDataBufferEntry() {} +}; + +struct SDataBufferEntry +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderBufferBase *> m_DataBuffer; + volatile QT3DSI32 mRefCount; + + SDataBufferEntry(NVRenderShaderProgram &inShader, const char *inBufferName) + : m_Shader(inShader) + , m_DataBuffer(inBufferName, inShader) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + void Set(qt3ds::render::NVRenderDataBuffer *inBuffer) + { + if (inBuffer) + inBuffer->Bind(); + + m_DataBuffer.Set(); + } + + static SDataBufferEntry CreateDataBufferEntry(NVRenderShaderProgram &inShader, + const char *inStem) + { + return SDataBufferEntry(inShader, inStem); + } +}; + +struct SEffectTextureData +{ + NVRenderTexture2D *m_Texture; + bool m_NeedsAlphaMultiply; + SEffectTextureData(NVRenderTexture2D *inTexture, bool inNeedsMultiply) + : m_Texture(inTexture) + , m_NeedsAlphaMultiply(inNeedsMultiply) + { + } + SEffectTextureData() + : m_Texture(NULL) + , m_NeedsAlphaMultiply(false) + { + } +}; + +struct STextureEntry +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Texture; + NVRenderCachedShaderProperty<QT3DSVec4> m_TextureData; + NVRenderCachedShaderProperty<QT3DSI32> m_TextureFlags; + volatile QT3DSI32 mRefCount; + + STextureEntry(NVRenderShaderProgram &inShader, const char *inTexName, const char *inDataName, + const char *inFlagName) + : m_Shader(inShader) + , m_Texture(inTexName, inShader) + , m_TextureData(inDataName, inShader) + , m_TextureFlags(inFlagName, inShader) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + void Set(NVRenderTexture2D *inTexture, bool inNeedsAlphaMultiply, + const SPropertyDefinition *inDefinition) + { + QT3DSF32 theMixValue(inNeedsAlphaMultiply ? 0.0f : 1.0f); + if (inTexture && inDefinition) { + inTexture->SetMagFilter(inDefinition->m_MagFilterOp); + inTexture->SetMinFilter( + static_cast<NVRenderTextureMinifyingOp::Enum>(inDefinition->m_MagFilterOp)); + inTexture->SetTextureWrapS(inDefinition->m_CoordOp); + inTexture->SetTextureWrapT(inDefinition->m_CoordOp); + } + m_Texture.Set(inTexture); + if (inTexture) { + STextureDetails theDetails(inTexture->GetTextureDetails()); + m_TextureData.Set( + QT3DSVec4((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height, theMixValue, 0.0f)); + // I have no idea what these flags do. + m_TextureFlags.Set(1); + } else + m_TextureFlags.Set(0); + } + + static STextureEntry CreateTextureEntry(NVRenderShaderProgram &inShader, const char *inStem, + Qt3DSString &inBuilder, Qt3DSString &inBuilder2) + { + inBuilder.assign(inStem); + inBuilder.append("Info"); + inBuilder2.assign("flag"); + inBuilder2.append(inStem); + return STextureEntry(inShader, inStem, inBuilder.c_str(), inBuilder2.c_str()); + } +}; + +typedef eastl::pair<CRegisteredString, NVScopedRefCounted<STextureEntry>> TNamedTextureEntry; +typedef eastl::pair<CRegisteredString, NVScopedRefCounted<SImageEntry>> TNamedImageEntry; +typedef eastl::pair<CRegisteredString, NVScopedRefCounted<SDataBufferEntry>> TNamedDataBufferEntry; +} + +namespace qt3ds { +namespace render { + + struct SEffectContext + { + CRegisteredString m_ClassName; + IQt3DSRenderContext &m_Context; + IResourceManager *m_ResourceManager; + nvvector<SAllocatedBufferEntry> m_AllocatedBuffers; + nvvector<SAllocatedImageEntry> m_AllocatedImages; + nvvector<SAllocatedDataBufferEntry> m_AllocatedDataBuffers; + nvvector<TNamedTextureEntry> m_TextureEntries; + nvvector<TNamedImageEntry> m_ImageEntries; + nvvector<TNamedDataBufferEntry> m_DataBufferEntries; + + SEffectContext(CRegisteredString inName, IQt3DSRenderContext &ctx, IResourceManager *inManager) + : m_ClassName(inName) + , m_Context(ctx) + , m_ResourceManager(inManager) + , m_AllocatedBuffers(ctx.GetAllocator(), "SEffectContext::m_AllocatedBuffers") + , m_AllocatedImages(ctx.GetAllocator(), "SEffectContext::m_AllocatedImages") + , m_AllocatedDataBuffers(ctx.GetAllocator(), "SEffectContext::m_AllocatedDataBuffers") + , m_TextureEntries(ctx.GetAllocator(), "SEffectContext::m_TextureEntries") + , m_ImageEntries(ctx.GetAllocator(), "SEffectContext::m_ImageEntries") + , m_DataBufferEntries(ctx.GetAllocator(), "SEffectContext::m_DataBufferEntries") + { + } + + ~SEffectContext() + { + while (m_AllocatedBuffers.size()) + ReleaseBuffer(0); + + while (m_AllocatedImages.size()) + ReleaseImage(0); + + while (m_AllocatedDataBuffers.size()) + ReleaseDataBuffer(0); + } + + void ReleaseBuffer(QT3DSU32 inIdx) + { + SAllocatedBufferEntry &theEntry(m_AllocatedBuffers[inIdx]); + theEntry.m_FrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer()); + m_ResourceManager->Release(*theEntry.m_FrameBuffer); + m_ResourceManager->Release(*theEntry.m_Texture); + m_AllocatedBuffers.replace_with_last(inIdx); + } + + void ReleaseImage(QT3DSU32 inIdx) + { + SAllocatedImageEntry &theEntry(m_AllocatedImages[inIdx]); + m_ResourceManager->Release(*theEntry.m_Image); + m_ResourceManager->Release(*theEntry.m_Texture); + m_AllocatedImages.replace_with_last(inIdx); + } + + void ReleaseDataBuffer(QT3DSU32 inIdx) + { + SAllocatedDataBufferEntry &theEntry(m_AllocatedDataBuffers[inIdx]); + m_Context.GetAllocator().deallocate(theEntry.m_BufferData.begin()); + + m_AllocatedDataBuffers.replace_with_last(inIdx); + } + + QT3DSU32 FindBuffer(CRegisteredString inName) + { + for (QT3DSU32 idx = 0, end = m_AllocatedBuffers.size(); idx < end; ++idx) + if (m_AllocatedBuffers[idx].m_Name == inName) + return idx; + return m_AllocatedBuffers.size(); + } + + QT3DSU32 FindImage(CRegisteredString inName) + { + for (QT3DSU32 idx = 0, end = m_AllocatedImages.size(); idx < end; ++idx) + if (m_AllocatedImages[idx].m_Name == inName) + return idx; + + return m_AllocatedImages.size(); + } + + QT3DSU32 FindDataBuffer(CRegisteredString inName) + { + for (QT3DSU32 idx = 0, end = m_AllocatedDataBuffers.size(); idx < end; ++idx) { + if (m_AllocatedDataBuffers[idx].m_Name == inName) + return idx; + } + + return m_AllocatedDataBuffers.size(); + } + + void SetTexture(NVRenderShaderProgram &inShader, CRegisteredString inPropName, + NVRenderTexture2D *inTexture, bool inNeedsMultiply, + Qt3DSString &inStringBuilder, Qt3DSString &inStringBuilder2, + const SPropertyDefinition *inPropDec = NULL) + { + STextureEntry *theTextureEntry(NULL); + for (QT3DSU32 idx = 0, end = m_TextureEntries.size(); idx < end && theTextureEntry == NULL; + ++idx) { + if (m_TextureEntries[idx].first == inPropName + && m_TextureEntries[idx].second->m_Shader.mPtr == &inShader) + theTextureEntry = m_TextureEntries[idx].second; + } + if (theTextureEntry == NULL) { + NVScopedRefCounted<STextureEntry> theNewEntry = QT3DS_NEW( + m_Context.GetAllocator(), STextureEntry)(STextureEntry::CreateTextureEntry( + inShader, inPropName, inStringBuilder, inStringBuilder2)); + m_TextureEntries.push_back(eastl::make_pair(inPropName, theNewEntry)); + theTextureEntry = theNewEntry.mPtr; + } + theTextureEntry->Set(inTexture, inNeedsMultiply, inPropDec); + } + + void SetImage(NVRenderShaderProgram &inShader, CRegisteredString inPropName, + NVRenderImage2D *inImage) + { + SImageEntry *theImageEntry(NULL); + for (QT3DSU32 idx = 0, end = m_ImageEntries.size(); idx < end && theImageEntry == NULL; + ++idx) { + if (m_ImageEntries[idx].first == inPropName + && m_ImageEntries[idx].second->m_Shader.mPtr == &inShader) + theImageEntry = m_ImageEntries[idx].second; + } + if (theImageEntry == NULL) { + NVScopedRefCounted<SImageEntry> theNewEntry = + QT3DS_NEW(m_Context.GetAllocator(), + SImageEntry)(SImageEntry::CreateImageEntry(inShader, inPropName)); + m_ImageEntries.push_back(eastl::make_pair(inPropName, theNewEntry)); + theImageEntry = theNewEntry.mPtr; + } + + theImageEntry->Set(inImage); + } + + void SetDataBuffer(NVRenderShaderProgram &inShader, CRegisteredString inPropName, + qt3ds::render::NVRenderDataBuffer *inBuffer) + { + SDataBufferEntry *theDataBufferEntry(NULL); + for (QT3DSU32 idx = 0, end = m_DataBufferEntries.size(); + idx < end && theDataBufferEntry == NULL; ++idx) { + if (m_DataBufferEntries[idx].first == inPropName + && m_DataBufferEntries[idx].second->m_Shader.mPtr == &inShader) + theDataBufferEntry = m_DataBufferEntries[idx].second; + } + if (theDataBufferEntry == NULL) { + NVScopedRefCounted<SDataBufferEntry> theNewEntry = + QT3DS_NEW(m_Context.GetAllocator(), SDataBufferEntry)( + SDataBufferEntry::CreateDataBufferEntry(inShader, inPropName)); + m_DataBufferEntries.push_back(eastl::make_pair(inPropName, theNewEntry)); + theDataBufferEntry = theNewEntry.mPtr; + } + + theDataBufferEntry->Set(inBuffer); + } + }; +} +} + +namespace { + +using qt3ds::render::NVRenderCachedShaderProperty; +/* We setup some shared state on the effect shaders */ +struct SEffectShader +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + NVRenderCachedShaderProperty<QT3DSVec2> m_FragColorAlphaSettings; + NVRenderCachedShaderProperty<QT3DSVec2> m_DestSize; + NVRenderCachedShaderProperty<QT3DSF32> m_AppFrame; + NVRenderCachedShaderProperty<QT3DSF32> m_FPS; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraClipRange; + STextureEntry m_TextureEntry; + volatile QT3DSI32 mRefCount; + SEffectShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_MVP("ModelViewProjectionMatrix", inShader) + , m_FragColorAlphaSettings("FragColorAlphaSettings", inShader) + , m_DestSize("DestSize", inShader) + , m_AppFrame("AppFrame", inShader) + , m_FPS("FPS", inShader) + , m_CameraClipRange("CameraClipRange", inShader) + , m_TextureEntry(inShader, "Texture0", "Texture0Info", "Texture0Flags") + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) +}; + +struct SEffectSystem : public IEffectSystem +{ + typedef nvhash_map<CRegisteredString, char8_t *> TPathDataMap; + typedef nvhash_set<CRegisteredString> TPathSet; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SEffectClass>> TEffectClassMap; + typedef nvhash_map<TStrStrPair, NVScopedRefCounted<SEffectShader>> TShaderMap; + typedef nvvector<SEffectContext *> TContextList; + + IQt3DSRenderContextCore &m_CoreContext; + IQt3DSRenderContext *m_Context; + NVScopedRefCounted<IResourceManager> m_ResourceManager; + mutable qt3ds::render::SPreAllocatedAllocator m_Allocator; + // Keep from dual-including headers. + TEffectClassMap m_EffectClasses; + nvvector<CRegisteredString> m_EffectList; + TContextList m_Contexts; + Qt3DSString m_TextureStringBuilder; + Qt3DSString m_TextureStringBuilder2; + TShaderMap m_ShaderMap; + NVScopedRefCounted<NVRenderDepthStencilState> m_DefaultStencilState; + nvvector<NVScopedRefCounted<NVRenderDepthStencilState>> m_DepthStencilStates; + volatile QT3DSI32 mRefCount; + + SEffectSystem(IQt3DSRenderContextCore &inContext) + : m_CoreContext(inContext) + , m_Context(NULL) + , m_Allocator(inContext.GetAllocator()) + , m_EffectClasses(inContext.GetAllocator(), "SEffectSystem::m_EffectClasses") + , m_EffectList(inContext.GetAllocator(), "SEffectSystem::m_EffectList") + , m_Contexts(inContext.GetAllocator(), "SEffectSystem::m_Contexts") + , m_ShaderMap(inContext.GetAllocator(), "SEffectSystem::m_ShaderMap") + , m_DepthStencilStates(inContext.GetAllocator(), "SEffectSystem::m_DepthStencilStates") + , mRefCount(0) + { + } + + ~SEffectSystem() + { + for (QT3DSU32 idx = 0, end = m_Contexts.size(); idx < end; ++idx) + NVDelete(m_Allocator, m_Contexts[idx]); + m_Contexts.clear(); + } + + SEffectContext &GetEffectContext(SEffect &inEffect) + { + if (inEffect.m_Context == NULL) { + inEffect.m_Context = + QT3DS_NEW(m_Allocator, SEffectContext)(inEffect.m_ClassName, + *m_Context, m_ResourceManager); + m_Contexts.push_back(inEffect.m_Context); + } + return *inEffect.m_Context; + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_CoreContext.GetAllocator()); + + SEffectClass *GetEffectClass(CRegisteredString inStr) + { + TEffectClassMap::iterator theIter = m_EffectClasses.find(inStr); + if (theIter != m_EffectClasses.end()) + return theIter->second; + return NULL; + } + const SEffectClass *GetEffectClass(CRegisteredString inStr) const + { + return const_cast<SEffectSystem *>(this)->GetEffectClass(inStr); + } + + bool IsEffectRegistered(CRegisteredString inStr) override + { + return GetEffectClass(inStr) != NULL; + } + NVConstDataRef<CRegisteredString> GetRegisteredEffects() override + { + m_EffectList.clear(); + for (TEffectClassMap::iterator theIter = m_EffectClasses.begin(), + theEnd = m_EffectClasses.end(); + theIter != theEnd; ++theIter) + m_EffectList.push_back(theIter->first); + return m_EffectList; + } + + // Registers an effect that runs via a single GLSL file. + bool RegisterGLSLEffect(CRegisteredString inName, const char8_t *inPathToEffect, + NVConstDataRef<SPropertyDeclaration> inProperties) override + { + if (IsEffectRegistered(inName)) + return false; + + m_CoreContext.GetDynamicObjectSystemCore().Register(inName, inProperties, sizeof(SEffect), + GraphObjectTypes::Effect); + IDynamicObjectClass &theClass = + *m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inName); + + SEffectClass *theEffect = QT3DS_NEW(m_Allocator, SEffectClass)(m_Allocator, theClass); + m_EffectClasses.insert(eastl::make_pair(inName, theEffect)); + + // Setup the commands required to run this effect + StaticAssert<(sizeof(SBindShader) % 4 == 0)>::valid_expression(); + StaticAssert<(sizeof(SApplyInstanceValue) % 4 == 0)>::valid_expression(); + StaticAssert<(sizeof(SRender) % 4 == 0)>::valid_expression(); + + QT3DSU32 commandAllocationSize = sizeof(SBindTarget); + commandAllocationSize += sizeof(SBindShader); + commandAllocationSize += sizeof(SApplyInstanceValue) * inProperties.size(); + commandAllocationSize += sizeof(SRender); + + QT3DSU32 commandCount = 3 + inProperties.size(); + QT3DSU32 commandPtrAllocationSize = commandCount * sizeof(SCommand *); + QT3DSU32 allocationSize = Align8(commandAllocationSize) + commandPtrAllocationSize; + QT3DSU8 *startBuffer = + (QT3DSU8 *)m_Allocator.allocate(allocationSize, "dynamic::SCommand", __FILE__, __LINE__); + QT3DSU8 *dataBuffer = startBuffer; + // Setup the command buffer such that the ptrs to the commands and the commands themselves + // are + // all in the same block of memory. This enables quicker iteration (trivially quicker, most + // likely) + // but it also enables simpler memory management (single free). + // Furthermore, for a single glsl file the effect properties map precisely into the + // glsl file itself. + SCommand **theFirstCommandPtr = + (reinterpret_cast<SCommand **>(dataBuffer + Align8(commandAllocationSize))); + SCommand **theCommandPtr = theFirstCommandPtr; + memZero(dataBuffer, commandAllocationSize); + + new (dataBuffer) SBindTarget(); + *theCommandPtr = (SCommand *)dataBuffer; + ++theCommandPtr; + dataBuffer += sizeof(SBindTarget); + + new (dataBuffer) SBindShader(m_CoreContext.GetStringTable().RegisterStr(inPathToEffect)); + *theCommandPtr = (SCommand *)dataBuffer; + ++theCommandPtr; + dataBuffer += sizeof(SBindShader); + + for (QT3DSU32 idx = 0, end = inProperties.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition( + theEffect->m_DynamicClass->GetProperties()[idx]); + new (dataBuffer) SApplyInstanceValue(theDefinition.m_Name, theDefinition.m_DataType, + theDefinition.m_Offset); + *theCommandPtr = (SCommand *)dataBuffer; + ++theCommandPtr; + dataBuffer += sizeof(SApplyInstanceValue); + } + new (dataBuffer) SRender(false); + *theCommandPtr = (SCommand *)dataBuffer; + ++theCommandPtr; + dataBuffer += sizeof(SRender); + // Ensure we end up *exactly* where we expected to. + QT3DS_ASSERT(dataBuffer == startBuffer + commandAllocationSize); + QT3DS_ASSERT(theCommandPtr - theFirstCommandPtr == (int)inProperties.size() + 3); + m_CoreContext.GetDynamicObjectSystemCore().SetRenderCommands( + inName, NVConstDataRef<SCommand *>(theFirstCommandPtr, commandCount)); + m_Allocator.deallocate(startBuffer); + return true; + } + + void SetEffectPropertyDefaultValue(CRegisteredString inName, + CRegisteredString inPropName, + NVConstDataRef<QT3DSU8> inDefaultData) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetPropertyDefaultValue(inName, inPropName, + inDefaultData); + } + + void SetEffectPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetPropertyEnumNames(inName, inPropName, + inNames); + } + + bool RegisterEffect(CRegisteredString inName, + NVConstDataRef<SPropertyDeclaration> inProperties) override + { + if (IsEffectRegistered(inName)) + return false; + m_CoreContext.GetDynamicObjectSystemCore().Register(inName, inProperties, sizeof(SEffect), + GraphObjectTypes::Effect); + IDynamicObjectClass &theClass = + *m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inName); + SEffectClass *theEffect = QT3DS_NEW(m_Allocator, SEffectClass)(m_Allocator, theClass); + m_EffectClasses.insert(eastl::make_pair(inName, theEffect)); + return true; + } + + bool UnregisterEffect(CRegisteredString inName) override + { + if (!IsEffectRegistered(inName)) + return false; + + m_CoreContext.GetDynamicObjectSystemCore().Unregister(inName); + + TEffectClassMap::iterator iter = m_EffectClasses.find(inName); + if (iter != m_EffectClasses.end()) + m_EffectClasses.erase(iter); + + for (QT3DSU32 idx = 0, end = m_Contexts.size(); idx < end; ++idx) { + if (m_Contexts[idx]->m_ClassName == inName) + ReleaseEffectContext(m_Contexts[idx]); + } + return true; + } + + virtual NVConstDataRef<CRegisteredString> + GetEffectPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName) const override + { + const SEffectClass *theClass = GetEffectClass(inName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + NVConstDataRef<CRegisteredString>(); + } + const SPropertyDefinition *theDefinitionPtr = + theClass->m_DynamicClass->FindPropertyByName(inPropName); + if (theDefinitionPtr) + return theDefinitionPtr->m_EnumValueNames; + return NVConstDataRef<CRegisteredString>(); + } + + virtual NVConstDataRef<SPropertyDefinition> + GetEffectProperties(CRegisteredString inEffectName) const override + { + const SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass) + return theClass->m_DynamicClass->GetProperties(); + return NVConstDataRef<SPropertyDefinition>(); + } + + void SetEffectPropertyTextureSettings(CRegisteredString inName, + CRegisteredString inPropName, + CRegisteredString inPropPath, + NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, + NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetPropertyTextureSettings( + inName, inPropName, inPropPath, inTexType, inCoordOp, inMagFilterOp, inMinFilterOp); + } + + void SetEffectRequiresDepthTexture(CRegisteredString inEffectName, bool inValue) override + { + SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + theClass->m_DynamicClass->SetRequiresDepthTexture(inValue); + } + + bool DoesEffectRequireDepthTexture(CRegisteredString inEffectName) const override + { + const SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return false; + } + return theClass->m_DynamicClass->RequiresDepthTexture(); + } + + void SetEffectRequiresCompilation(CRegisteredString inEffectName, bool inValue) override + { + SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return; + } + theClass->m_DynamicClass->SetRequiresCompilation(inValue); + } + + bool DoesEffectRequireCompilation(CRegisteredString inEffectName) const override + { + const SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass == NULL) { + QT3DS_ASSERT(false); + return false; + } + return theClass->m_DynamicClass->RequiresCompilation(); + } + + void SetEffectCommands(CRegisteredString inEffectName, + NVConstDataRef<dynamic::SCommand *> inCommands) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetRenderCommands(inEffectName, inCommands); + } + + virtual NVConstDataRef<dynamic::SCommand *> + GetEffectCommands(CRegisteredString inEffectName) const override + { + return m_CoreContext.GetDynamicObjectSystemCore().GetRenderCommands(inEffectName); + } + + SEffect *CreateEffectInstance(CRegisteredString inEffectName, + NVAllocatorCallback &inSceneGraphAllocator) override + { + SEffectClass *theClass = GetEffectClass(inEffectName); + if (theClass == NULL) + return NULL; + StaticAssert<(sizeof(SEffect) % 4 == 0)>::valid_expression(); + + SEffect *theEffect = (SEffect *)m_CoreContext.GetDynamicObjectSystemCore().CreateInstance( + inEffectName, inSceneGraphAllocator); + theEffect->Initialize(); + return theEffect; + } + + void AllocateBuffer(SEffect &inEffect, const SAllocateBuffer &inCommand, QT3DSU32 inFinalWidth, + QT3DSU32 inFinalHeight, NVRenderTextureFormats::Enum inSourceTextureFormat) + { + // Check to see if it is already allocated and if it is, is it the correct size. If both of + // these assumptions hold, then we are good. + NVRenderTexture2D *theBufferTexture = NULL; + QT3DSU32 theWidth = + ITextRenderer::NextMultipleOf4((QT3DSU32)(inFinalWidth * inCommand.m_SizeMultiplier)); + QT3DSU32 theHeight = + ITextRenderer::NextMultipleOf4((QT3DSU32)(inFinalHeight * inCommand.m_SizeMultiplier)); + NVRenderTextureFormats::Enum resultFormat = inCommand.m_Format; + if (resultFormat == NVRenderTextureFormats::Unknown) + resultFormat = inSourceTextureFormat; + + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + // size intentionally requiried every loop; + QT3DSU32 bufferIdx = theContext.FindBuffer(inCommand.m_Name); + if (bufferIdx < theContext.m_AllocatedBuffers.size()) { + SAllocatedBufferEntry &theEntry(theContext.m_AllocatedBuffers[bufferIdx]); + STextureDetails theDetails = theEntry.m_Texture->GetTextureDetails(); + if (theDetails.m_Width == theWidth && theDetails.m_Height == theHeight + && theDetails.m_Format == resultFormat) { + theBufferTexture = theEntry.m_Texture; + } else { + theContext.ReleaseBuffer(bufferIdx); + } + } + } + if (theBufferTexture == NULL) { + SEffectContext &theContext(GetEffectContext(inEffect)); + NVRenderFrameBuffer *theFB(m_ResourceManager->AllocateFrameBuffer()); + NVRenderTexture2D *theTexture( + m_ResourceManager->AllocateTexture2D(theWidth, theHeight, resultFormat)); + theTexture->SetMagFilter(inCommand.m_FilterOp); + theTexture->SetMinFilter( + static_cast<NVRenderTextureMinifyingOp::Enum>(inCommand.m_FilterOp)); + theTexture->SetTextureWrapS(inCommand.m_TexCoordOp); + theTexture->SetTextureWrapT(inCommand.m_TexCoordOp); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *theTexture); + theContext.m_AllocatedBuffers.push_back(SAllocatedBufferEntry( + inCommand.m_Name, *theFB, *theTexture, inCommand.m_BufferFlags)); + theBufferTexture = theTexture; + } + } + + void AllocateImage(SEffect &inEffect, const SAllocateImage &inCommand, QT3DSU32 inFinalWidth, + QT3DSU32 inFinalHeight) + { + NVRenderImage2D *theImage = NULL; + QT3DSU32 theWidth = + ITextRenderer::NextMultipleOf4((QT3DSU32)(inFinalWidth * inCommand.m_SizeMultiplier)); + QT3DSU32 theHeight = + ITextRenderer::NextMultipleOf4((QT3DSU32)(inFinalHeight * inCommand.m_SizeMultiplier)); + + QT3DS_ASSERT(inCommand.m_Format != NVRenderTextureFormats::Unknown); + + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + // size intentionally requiried every loop; + QT3DSU32 imageIdx = theContext.FindImage(inCommand.m_Name); + if (imageIdx < theContext.m_AllocatedImages.size()) { + SAllocatedImageEntry &theEntry(theContext.m_AllocatedImages[imageIdx]); + STextureDetails theDetails = theEntry.m_Texture->GetTextureDetails(); + if (theDetails.m_Width == theWidth && theDetails.m_Height == theHeight + && theDetails.m_Format == inCommand.m_Format) { + theImage = theEntry.m_Image; + } else { + theContext.ReleaseImage(imageIdx); + } + } + } + + if (theImage == NULL) { + SEffectContext &theContext(GetEffectContext(inEffect)); + // allocate an immutable texture + NVRenderTexture2D *theTexture(m_ResourceManager->AllocateTexture2D( + theWidth, theHeight, inCommand.m_Format, 1, true)); + theTexture->SetMagFilter(inCommand.m_FilterOp); + theTexture->SetMinFilter( + static_cast<NVRenderTextureMinifyingOp::Enum>(inCommand.m_FilterOp)); + theTexture->SetTextureWrapS(inCommand.m_TexCoordOp); + theTexture->SetTextureWrapT(inCommand.m_TexCoordOp); + NVRenderImage2D *theImage = + (m_ResourceManager->AllocateImage2D(theTexture, inCommand.m_Access)); + theContext.m_AllocatedImages.push_back(SAllocatedImageEntry( + inCommand.m_Name, *theImage, *theTexture, inCommand.m_BufferFlags)); + } + } + + void AllocateDataBuffer(SEffect &inEffect, const SAllocateDataBuffer &inCommand) + { + QT3DSU32 theBufferSize = (QT3DSU32)inCommand.m_Size; + QT3DS_ASSERT(theBufferSize); + qt3ds::render::NVRenderDataBuffer *theDataBuffer = NULL; + qt3ds::render::NVRenderDataBuffer *theDataWrapBuffer = NULL; + + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + // size intentionally requiried every loop; + QT3DSU32 bufferIdx = theContext.FindDataBuffer(inCommand.m_Name); + if (bufferIdx < theContext.m_AllocatedDataBuffers.size()) { + SAllocatedDataBufferEntry &theEntry(theContext.m_AllocatedDataBuffers[bufferIdx]); + if (theEntry.m_BufferType == inCommand.m_DataBufferType + && theEntry.m_BufferData.size() == theBufferSize) { + theDataBuffer = theEntry.m_DataBuffer; + } else { + // if type and size don't match something is wrong + QT3DS_ASSERT(false); + } + } + } + + if (theDataBuffer == NULL) { + SEffectContext &theContext(GetEffectContext(inEffect)); + NVRenderContext &theRenderContext(m_Context->GetRenderContext()); + QT3DSU8 *initialData = (QT3DSU8 *)theContext.m_Context.GetAllocator().allocate( + theBufferSize, "SEffectContext::AllocateDataBuffer", __FILE__, __LINE__); + NVDataRef<QT3DSU8> data((QT3DSU8 *)initialData, theBufferSize); + memset(initialData, 0x0L, theBufferSize); + if (inCommand.m_DataBufferType == NVRenderBufferBindValues::Storage) { + theDataBuffer = theRenderContext.CreateStorageBuffer( + inCommand.m_Name, qt3ds::render::NVRenderBufferUsageType::Dynamic, theBufferSize, + data, NULL); + } else if (inCommand.m_DataBufferType == NVRenderBufferBindValues::Draw_Indirect) { + QT3DS_ASSERT(theBufferSize == sizeof(qt3ds::render::DrawArraysIndirectCommand)); + // init a draw call + QT3DSU32 *pIndirectDrawCall = (QT3DSU32 *)initialData; + // vertex count we draw points right now only + // the rest we fill in by GPU + pIndirectDrawCall[0] = 1; + theDataBuffer = theRenderContext.CreateDrawIndirectBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, theBufferSize, data); + } else + QT3DS_ASSERT(false); + + theContext.m_AllocatedDataBuffers.push_back(SAllocatedDataBufferEntry( + inCommand.m_Name, *theDataBuffer, inCommand.m_DataBufferType, data, + inCommand.m_BufferFlags)); + + // create wrapper buffer + if (inCommand.m_DataBufferWrapType == NVRenderBufferBindValues::Storage + && inCommand.m_WrapName && theDataBuffer) { + theDataWrapBuffer = theRenderContext.CreateStorageBuffer( + inCommand.m_WrapName, qt3ds::render::NVRenderBufferUsageType::Dynamic, + theBufferSize, data, theDataBuffer); + theContext.m_AllocatedDataBuffers.push_back(SAllocatedDataBufferEntry( + inCommand.m_WrapName, *theDataWrapBuffer, inCommand.m_DataBufferWrapType, + NVDataRef<QT3DSU8>(), inCommand.m_BufferFlags)); + } + } + } + + NVRenderTexture2D *FindTexture(SEffect &inEffect, CRegisteredString inName) + { + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + QT3DSU32 bufferIdx = theContext.FindBuffer(inName); + if (bufferIdx < theContext.m_AllocatedBuffers.size()) { + return theContext.m_AllocatedBuffers[bufferIdx].m_Texture; + } + } + QT3DS_ASSERT(false); + return NULL; + } + + NVRenderFrameBuffer *BindBuffer(SEffect &inEffect, const SBindBuffer &inCommand, + QT3DSMat44 &outMVP, QT3DSVec2 &outDestSize) + { + NVRenderFrameBuffer *theBuffer = NULL; + NVRenderTexture2D *theTexture = NULL; + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + QT3DSU32 bufferIdx = theContext.FindBuffer(inCommand.m_BufferName); + if (bufferIdx < theContext.m_AllocatedBuffers.size()) { + theBuffer = theContext.m_AllocatedBuffers[bufferIdx].m_FrameBuffer; + theTexture = theContext.m_AllocatedBuffers[bufferIdx].m_Texture; + theContext.m_AllocatedBuffers[bufferIdx].m_NeedsClear = false; + } + } + if (theBuffer == NULL) { + qCCritical(INVALID_OPERATION, "Effect %s: Failed to find buffer %s for bind", + inEffect.m_ClassName.c_str(), inCommand.m_BufferName.c_str()); + QString errorMsg = QObject::tr("Failed to compile \"%1\" effect.\nConsider" + " removing it from the presentation.") + .arg(inEffect.m_ClassName.c_str()); + QT3DS_ALWAYS_ASSERT_MESSAGE(errorMsg.toUtf8()); + outMVP = QT3DSMat44::createIdentity(); + return NULL; + } + + if (theTexture) { + SCamera::SetupOrthographicCameraForOffscreenRender(*theTexture, outMVP); + STextureDetails theDetails(theTexture->GetTextureDetails()); + m_Context->GetRenderContext().SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + outDestSize = QT3DSVec2((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height); + } + + return theBuffer; + } + + SEffectShader *BindShader(CRegisteredString &inEffectId, const SBindShader &inCommand) + { + SEffectClass *theClass = GetEffectClass(inEffectId); + if (!theClass) { + QT3DS_ASSERT(false); + return NULL; + } + + bool forceCompilation = theClass->m_DynamicClass->RequiresCompilation(); + + eastl::pair<const TStrStrPair, NVScopedRefCounted<SEffectShader>> theInserter( + TStrStrPair(inCommand.m_ShaderPath, inCommand.m_ShaderDefine), + NVScopedRefCounted<SEffectShader>()); + eastl::pair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert(theInserter)); + + if (theInsertResult.second || forceCompilation) { + NVRenderShaderProgram *theProgram = + m_Context->GetDynamicObjectSystem() + .GetShaderProgram(inCommand.m_ShaderPath, inCommand.m_ShaderDefine, + TShaderFeatureSet(), SDynamicShaderProgramFlags(), + forceCompilation).first; + if (theProgram) + theInsertResult.first->second = QT3DS_NEW(m_Allocator, SEffectShader)(*theProgram); + } + if (theInsertResult.first->second) { + NVRenderContext &theContext(m_Context->GetRenderContext()); + theContext.SetActiveShader(theInsertResult.first->second->m_Shader); + } + + return theInsertResult.first->second; + } + + void DoApplyInstanceValue(SEffect &inEffect, QT3DSU8 *inDataPtr, CRegisteredString inPropertyName, + NVRenderShaderDataTypes::Enum inPropertyType, + NVRenderShaderProgram &inShader, + const SPropertyDefinition &inDefinition) + { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inPropertyName); + using namespace qt3ds::render; + if (theConstant) { + if (theConstant->GetShaderConstantType() == inPropertyType) { + if (inPropertyType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + StaticAssert<sizeof(CRegisteredString) + == sizeof(NVRenderTexture2DPtr)>::valid_expression(); + CRegisteredString *theStrPtr = reinterpret_cast<CRegisteredString *>(inDataPtr); + IBufferManager &theBufferManager(m_Context->GetBufferManager()); + IOffscreenRenderManager &theOffscreenRenderer( + m_Context->GetOffscreenRenderManager()); + bool needsAlphaMultiply = true; + NVRenderTexture2D *theTexture = nullptr; + if (theStrPtr->IsValid()) { + if (theOffscreenRenderer.HasOffscreenRenderer(*theStrPtr)) { + SOffscreenRenderResult theResult + = theOffscreenRenderer.GetRenderedItem(*theStrPtr); + needsAlphaMultiply = false; + theTexture = theResult.m_Texture; + } else { + SImageTextureData theTextureData + = theBufferManager.LoadRenderImage(*theStrPtr); + needsAlphaMultiply = true; + theTexture = theTextureData.m_Texture; + } + } + GetEffectContext(inEffect).SetTexture( + inShader, inPropertyName, theTexture, needsAlphaMultiply, + m_TextureStringBuilder, m_TextureStringBuilder2, &inDefinition); + } else if (inPropertyType == NVRenderShaderDataTypes::NVRenderImage2DPtr) { + StaticAssert<sizeof(CRegisteredString) + == sizeof(NVRenderTexture2DPtr)>::valid_expression(); + NVRenderImage2D *theImage = NULL; + GetEffectContext(inEffect).SetImage(inShader, inPropertyName, theImage); + } else if (inPropertyType == NVRenderShaderDataTypes::NVRenderDataBufferPtr) { + // we don't handle this here + } else { + switch (inPropertyType) { +#define HANDLE_QT3DS_SHADER_DATA_TYPE(type) \ + case NVRenderShaderDataTypes::type: \ + inShader.SetPropertyValue(theConstant, *(reinterpret_cast<type *>(inDataPtr))); \ + break; + ITERATE_QT3DS_SHADER_DATA_TYPES +#undef HANDLE_QT3DS_SHADER_DATA_TYPE + default: + QT3DS_ASSERT(false); + break; + } + } + } else { + qCCritical(INVALID_OPERATION, + "Effect ApplyInstanceValue command datatype " + "and shader datatypes differ for property %s", + inPropertyName.c_str()); + QT3DS_ASSERT(false); + } + } + } + + void ApplyInstanceValue(SEffect &inEffect, SEffectClass &inClass, + NVRenderShaderProgram &inShader, const SApplyInstanceValue &inCommand) + { + // sanity check + if (inCommand.m_PropertyName.IsValid()) { + bool canGetData = + inCommand.m_ValueOffset + getSizeofShaderDataType(inCommand.m_ValueType) + <= inEffect.m_DataSectionByteSize; + if (canGetData == false) { + QT3DS_ASSERT(false); + return; + } + QT3DSU8 *dataPtr = inEffect.GetDataSectionBegin() + inCommand.m_ValueOffset; + const SPropertyDefinition *theDefinition = + inClass.m_DynamicClass->FindPropertyByName(inCommand.m_PropertyName); + if (theDefinition) + DoApplyInstanceValue(inEffect, dataPtr, inCommand.m_PropertyName, + inCommand.m_ValueType, inShader, *theDefinition); + } else { + NVConstDataRef<SPropertyDefinition> theDefs = inClass.m_DynamicClass->GetProperties(); + for (QT3DSU32 idx = 0, end = theDefs.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theDefs[idx]); + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(theDefinition.m_Name); + + // This is fine, the property wasn't found and we continue, no problem. + if (!theConstant) + continue; + QT3DSU8 *dataPtr = inEffect.GetDataSectionBegin() + theDefinition.m_Offset; + DoApplyInstanceValue(inEffect, dataPtr, theDefinition.m_Name, + theDefinition.m_DataType, inShader, theDefinition); + } + } + } + + void ApplyValue(SEffect &inEffect, SEffectClass &inClass, NVRenderShaderProgram &inShader, + const SApplyValue &inCommand) + { + if (inCommand.m_PropertyName.IsValid()) { + QT3DSU8 *dataPtr = inCommand.m_Value.mData; + const SPropertyDefinition *theDefinition = + inClass.m_DynamicClass->FindPropertyByName(inCommand.m_PropertyName); + if (theDefinition) + DoApplyInstanceValue(inEffect, dataPtr, inCommand.m_PropertyName, + inCommand.m_ValueType, inShader, *theDefinition); + } + } + + bool ApplyBlending(const SApplyBlending &inCommand) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + + theContext.SetBlendingEnabled(true); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc = + qt3ds::render::NVRenderBlendFunctionArgument( + inCommand.m_SrcBlendFunc, inCommand.m_DstBlendFunc, inCommand.m_SrcBlendFunc, + inCommand.m_DstBlendFunc); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + theContext.SetBlendFunction(blendFunc); + theContext.SetBlendEquation(blendEqu); + + return true; + } + + // This has the potential to change the source texture for the current render pass + SEffectTextureData ApplyBufferValue(SEffect &inEffect, NVRenderShaderProgram &inShader, + const SApplyBufferValue &inCommand, + NVRenderTexture2D &inSourceTexture, + SEffectTextureData inCurrentSourceTexture) + { + SEffectTextureData theTextureToBind; + if (inCommand.m_BufferName.IsValid()) { + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + QT3DSU32 bufferIdx = theContext.FindBuffer(inCommand.m_BufferName); + if (bufferIdx < theContext.m_AllocatedBuffers.size()) { + SAllocatedBufferEntry &theEntry(theContext.m_AllocatedBuffers[bufferIdx]); + if (theEntry.m_NeedsClear) { + NVRenderContext &theRenderContext(m_Context->GetRenderContext()); + + theRenderContext.SetRenderTarget(theEntry.m_FrameBuffer); + // Note that depth/stencil buffers need an explicit clear in their bind + // commands in order to ensure + // we clear the least amount of information possible. + + if (theEntry.m_Texture) { + NVRenderTextureFormats::Enum theTextureFormat = + theEntry.m_Texture->GetTextureDetails().m_Format; + if (theTextureFormat != NVRenderTextureFormats::Depth16 + && theTextureFormat != NVRenderTextureFormats::Depth24 + && theTextureFormat != NVRenderTextureFormats::Depth32 + && theTextureFormat != NVRenderTextureFormats::Depth24Stencil8) { + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theRenderContext, &NVRenderContext::GetClearColor, + &NVRenderContext::SetClearColor, QT3DSVec4(0.0f)); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + } + } + theEntry.m_NeedsClear = false; + } + theTextureToBind = SEffectTextureData(theEntry.m_Texture, false); + } + } + if (theTextureToBind.m_Texture == NULL) { + QT3DS_ASSERT(false); + qCCritical(INVALID_OPERATION, "Effect %s: Failed to find buffer %s for bind", + inEffect.m_ClassName.c_str(), inCommand.m_BufferName.c_str()); + QT3DS_ASSERT(false); + } + } else // no name means bind the source + theTextureToBind = SEffectTextureData(&inSourceTexture, false); + + if (inCommand.m_ParamName.IsValid()) { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inCommand.m_ParamName); + + if (theConstant) { + if (theConstant->GetShaderConstantType() + != NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + qCCritical(INVALID_OPERATION, + "Effect %s: Binding buffer to parameter %s that is not a texture", + inEffect.m_ClassName.c_str(), inCommand.m_ParamName.c_str()); + QT3DS_ASSERT(false); + } else { + GetEffectContext(inEffect).SetTexture( + inShader, inCommand.m_ParamName, theTextureToBind.m_Texture, + theTextureToBind.m_NeedsAlphaMultiply, m_TextureStringBuilder, + m_TextureStringBuilder2); + } + } + return inCurrentSourceTexture; + } else { + return theTextureToBind; + } + } + + void ApplyDepthValue(SEffect &inEffect, NVRenderShaderProgram &inShader, + const SApplyDepthValue &inCommand, NVRenderTexture2D *inTexture) + { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inCommand.m_ParamName); + + if (theConstant) { + if (theConstant->GetShaderConstantType() + != NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + qCCritical(INVALID_OPERATION, + "Effect %s: Binding buffer to parameter %s that is not a texture", + inEffect.m_ClassName.c_str(), inCommand.m_ParamName.c_str()); + QT3DS_ASSERT(false); + } else { + GetEffectContext(inEffect).SetTexture(inShader, inCommand.m_ParamName, inTexture, + false, m_TextureStringBuilder, + m_TextureStringBuilder2); + } + } + } + + void ApplyImageValue(SEffect &inEffect, NVRenderShaderProgram &inShader, + const SApplyImageValue &inCommand) + { + SAllocatedImageEntry theImageToBind; + if (inCommand.m_ImageName.IsValid()) { + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + QT3DSU32 bufferIdx = theContext.FindImage(inCommand.m_ImageName); + if (bufferIdx < theContext.m_AllocatedImages.size()) { + theImageToBind = SAllocatedImageEntry(theContext.m_AllocatedImages[bufferIdx]); + } + } + } + + if (theImageToBind.m_Image == NULL) { + qCCritical(INVALID_OPERATION, "Effect %s: Failed to find image %s for bind", + inEffect.m_ClassName.c_str(), inCommand.m_ImageName.c_str()); + QT3DS_ASSERT(false); + } + + if (inCommand.m_ParamName.IsValid()) { + qt3ds::render::NVRenderShaderConstantBase *theConstant = + inShader.GetShaderConstant(inCommand.m_ParamName); + + if (theConstant) { + if (inCommand.m_NeedSync) { + NVRenderBufferBarrierFlags flags( + qt3ds::render::NVRenderBufferBarrierValues::TextureFetch + | qt3ds::render::NVRenderBufferBarrierValues::TextureUpdate); + inShader.GetRenderContext().SetMemoryBarrier(flags); + } + + if (theConstant->GetShaderConstantType() + == NVRenderShaderDataTypes::NVRenderImage2DPtr + && !inCommand.m_BindAsTexture) { + GetEffectContext(inEffect).SetImage(inShader, inCommand.m_ParamName, + theImageToBind.m_Image); + } else if (theConstant->GetShaderConstantType() + == NVRenderShaderDataTypes::NVRenderTexture2DPtr + && inCommand.m_BindAsTexture) { + GetEffectContext(inEffect).SetTexture( + inShader, inCommand.m_ParamName, theImageToBind.m_Texture, false, + m_TextureStringBuilder, m_TextureStringBuilder2); + } else { + qCCritical(INVALID_OPERATION, + "Effect %s: Binding buffer to parameter %s that is not a texture", + inEffect.m_ClassName.c_str(), inCommand.m_ParamName.c_str()); + QT3DS_ASSERT(false); + } + } + } + } + + void ApplyDataBufferValue(SEffect &inEffect, NVRenderShaderProgram &inShader, + const SApplyDataBufferValue &inCommand) + { + SAllocatedDataBufferEntry theBufferToBind; + if (inCommand.m_ParamName.IsValid()) { + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + QT3DSU32 bufferIdx = theContext.FindDataBuffer(inCommand.m_ParamName); + if (bufferIdx < theContext.m_AllocatedDataBuffers.size()) { + theBufferToBind = + SAllocatedDataBufferEntry(theContext.m_AllocatedDataBuffers[bufferIdx]); + if (theBufferToBind.m_NeedsClear) { + NVDataRef<QT3DSU8> pData = theBufferToBind.m_DataBuffer->MapBuffer(); + memset(pData.begin(), 0x0L, theBufferToBind.m_BufferData.size()); + theBufferToBind.m_DataBuffer->UnmapBuffer(); + theBufferToBind.m_NeedsClear = false; + } + } + } + + if (theBufferToBind.m_DataBuffer == NULL) { + qCCritical(INVALID_OPERATION, "Effect %s: Failed to find buffer %s for bind", + inEffect.m_ClassName.c_str(), inCommand.m_ParamName.c_str()); + QT3DS_ASSERT(false); + } + + qt3ds::render::NVRenderShaderBufferBase *theConstant = + inShader.GetShaderBuffer(inCommand.m_ParamName); + + if (theConstant) { + GetEffectContext(inEffect).SetDataBuffer(inShader, inCommand.m_ParamName, + theBufferToBind.m_DataBuffer); + } else if (theBufferToBind.m_BufferType + == qt3ds::render::NVRenderBufferBindValues::Draw_Indirect) { + // since we filled part of this buffer on the GPU we need a sync before usage + NVRenderBufferBarrierFlags flags( + qt3ds::render::NVRenderBufferBarrierValues::CommandBuffer); + inShader.GetRenderContext().SetMemoryBarrier(flags); + } + } + } + + void ApplyRenderStateValue(NVRenderFrameBuffer *inTarget, + NVRenderTexture2D *inDepthStencilTexture, + const SApplyRenderState &theCommand) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + QT3DSU32 inState = (QT3DSU32)theCommand.m_RenderState; + bool inEnable = theCommand.m_Enabled; + + switch (inState) { + case NVRenderState::StencilTest: { + if (inEnable && inTarget) { + inTarget->Attach(NVRenderFrameBufferAttachments::DepthStencil, + *inDepthStencilTexture); + } else if (inTarget) { + inTarget->Attach(NVRenderFrameBufferAttachments::DepthStencil, + NVRenderTextureOrRenderBuffer()); + } + + theContext.SetStencilTestEnabled(inEnable); + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + + static bool CompareDepthStencilState(NVRenderDepthStencilState &inState, + SDepthStencil &inStencil) + { + qt3ds::render::NVRenderStencilFunctionArgument theFunction = + inState.GetStencilFunc(qt3ds::render::NVRenderFaces::Front); + qt3ds::render::NVRenderStencilOperationArgument theOperation = + inState.GetStencilOp(qt3ds::render::NVRenderFaces::Front); + + return theFunction.m_Function == inStencil.m_StencilFunction + && theFunction.m_Mask == inStencil.m_Mask + && theFunction.m_ReferenceValue == inStencil.m_Reference + && theOperation.m_StencilFail == inStencil.m_StencilFailOperation + && theOperation.m_DepthFail == inStencil.m_DepthFailOperation + && theOperation.m_DepthPass == inStencil.m_DepthPassOperation; + } + + void RenderPass(SEffectShader &inShader, const QT3DSMat44 &inMVP, + SEffectTextureData inSourceTexture, NVRenderFrameBuffer *inFrameBuffer, + QT3DSVec2 &inDestSize, const QT3DSVec2 &inCameraClipRange, + NVRenderTexture2D *inDepthStencil, Option<SDepthStencil> inDepthStencilCommand, + bool drawIndirect) + { + NVRenderContext &theContext(m_Context->GetRenderContext()); + theContext.SetRenderTarget(inFrameBuffer); + if (inDepthStencil && inFrameBuffer) { + inFrameBuffer->Attach(NVRenderFrameBufferAttachments::DepthStencil, *inDepthStencil); + if (inDepthStencilCommand.hasValue()) { + SDepthStencil &theDepthStencil(*inDepthStencilCommand); + QT3DSU32 clearFlags = 0; + if (theDepthStencil.m_Flags.HasClearStencil()) + clearFlags |= NVRenderClearValues::Stencil; + if (theDepthStencil.m_Flags.HasClearDepth()) + clearFlags |= NVRenderClearValues::Depth; + + if (clearFlags) + theContext.Clear(qt3ds::render::NVRenderClearFlags(clearFlags)); + + NVRenderDepthStencilState *targetState = NULL; + for (QT3DSU32 idx = 0, end = m_DepthStencilStates.size(); + idx < end && targetState == NULL; ++idx) { + NVRenderDepthStencilState &theState = *m_DepthStencilStates[idx]; + if (CompareDepthStencilState(theState, theDepthStencil)) + targetState = &theState; + } + if (targetState == NULL) { + qt3ds::render::NVRenderStencilFunctionArgument theFunctionArg( + theDepthStencil.m_StencilFunction, theDepthStencil.m_Reference, + theDepthStencil.m_Mask); + qt3ds::render::NVRenderStencilOperationArgument theOpArg( + theDepthStencil.m_StencilFailOperation, + theDepthStencil.m_DepthFailOperation, theDepthStencil.m_DepthPassOperation); + targetState = theContext.CreateDepthStencilState( + theContext.IsDepthTestEnabled(), theContext.IsDepthWriteEnabled(), + theContext.GetDepthFunction(), true, theFunctionArg, theFunctionArg, + theOpArg, theOpArg); + m_DepthStencilStates.push_back(targetState); + } + theContext.SetDepthStencilState(targetState); + } + } + + theContext.SetActiveShader(inShader.m_Shader); + inShader.m_MVP.Set(inMVP); + if (inSourceTexture.m_Texture) { + inShader.m_TextureEntry.Set(inSourceTexture.m_Texture, + inSourceTexture.m_NeedsAlphaMultiply, NULL); + } else { + qCCritical(INTERNAL_ERROR, "Failed to setup pass due to null source texture"); + QT3DS_ASSERT(false); + } + inShader.m_FragColorAlphaSettings.Set(QT3DSVec2(1.0f, 0.0f)); + inShader.m_DestSize.Set(inDestSize); + if (inShader.m_AppFrame.IsValid()) + inShader.m_AppFrame.Set((QT3DSF32)m_Context->GetFrameCount()); + if (inShader.m_FPS.IsValid()) + inShader.m_FPS.Set((QT3DSF32)m_Context->GetFPS().first); + if (inShader.m_CameraClipRange.IsValid()) + inShader.m_CameraClipRange.Set(inCameraClipRange); + + if (!drawIndirect) + m_Context->GetRenderer().RenderQuad(); + else + m_Context->GetRenderer().RenderPointsIndirect(); + + if (inDepthStencil && inFrameBuffer) { + inFrameBuffer->Attach(NVRenderFrameBufferAttachments::DepthStencil, + NVRenderTextureOrRenderBuffer()); + theContext.SetDepthStencilState(m_DefaultStencilState); + } + } + + void DoRenderEffect(SEffect &inEffect, SEffectClass &inClass, + NVRenderTexture2D &inSourceTexture, QT3DSMat44 &inMVP, + NVRenderFrameBuffer *inTarget, bool inEnableBlendWhenRenderToTarget, + NVRenderTexture2D *inDepthTexture, NVRenderTexture2D *inDepthStencilTexture, + const QT3DSVec2 inCameraClipRange) + { + // Run through the effect commands and render the effect. + // NVRenderTexture2D* theCurrentTexture(&inSourceTexture); + NVRenderContext &theContext = m_Context->GetRenderContext(); + + // Context variables that are updated during the course of a pass. + SEffectTextureData theCurrentSourceTexture(&inSourceTexture, false); + NVRenderTexture2D *theCurrentDepthStencilTexture = NULL; + NVRenderFrameBuffer *theCurrentRenderTarget(inTarget); + SEffectShader *theCurrentShader(NULL); + NVRenderRect theOriginalViewport(theContext.GetViewport()); + bool wasScissorEnabled = theContext.IsScissorTestEnabled(); + bool wasBlendingEnabled = theContext.IsBlendingEnabled(); + // save current blending setup + qt3ds::render::NVRenderBlendFunctionArgument theBlendFunc = theContext.GetBlendFunction(); + qt3ds::render::NVRenderBlendEquationArgument theBlendEqu = theContext.GetBlendEquation(); + bool intermediateBlendingEnabled = false; + STextureDetails theDetails(inSourceTexture.GetTextureDetails()); + QT3DSU32 theFinalWidth = (QT3DSU32)(theDetails.m_Width); + QT3DSU32 theFinalHeight = (QT3DSU32)(theDetails.m_Height); + QT3DSVec2 theDestSize; + { + // Ensure no matter the command run goes we replace the rendering system to some + // semblance of the approprate + // setting. + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + NVRenderContextScopedProperty<bool> __stencilTest( + theContext, &NVRenderContext::IsStencilTestEnabled, + &NVRenderContext::SetStencilTestEnabled); + NVRenderContextScopedProperty<qt3ds::render::NVRenderBoolOp::Enum> __depthFunction( + theContext, &NVRenderContext::GetDepthFunction, &NVRenderContext::SetDepthFunction); + Option<SDepthStencil> theCurrentDepthStencil; + + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(false); + theContext.SetCullingEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetDepthWriteEnabled(false); + + QT3DSMat44 theMVP(QT3DSMat44::createIdentity()); + NVConstDataRef<dynamic::SCommand *> theCommands = + inClass.m_DynamicClass->GetRenderCommands(); + for (QT3DSU32 commandIdx = 0, commandEnd = theCommands.size(); commandIdx < commandEnd; + ++commandIdx) { + const SCommand &theCommand(*theCommands[commandIdx]); + switch (theCommand.m_Type) { + case CommandTypes::AllocateBuffer: + AllocateBuffer(inEffect, static_cast<const SAllocateBuffer &>(theCommand), + theFinalWidth, theFinalHeight, theDetails.m_Format); + break; + + case CommandTypes::AllocateImage: + AllocateImage(inEffect, static_cast<const SAllocateImage &>(theCommand), + theFinalWidth, theFinalHeight); + break; + + case CommandTypes::AllocateDataBuffer: + AllocateDataBuffer(inEffect, + static_cast<const SAllocateDataBuffer &>(theCommand)); + break; + + case CommandTypes::BindBuffer: + theCurrentRenderTarget = + BindBuffer(inEffect, static_cast<const SBindBuffer &>(theCommand), theMVP, + theDestSize); + break; + + case CommandTypes::BindTarget: { + m_Context->GetRenderContext().SetRenderTarget(inTarget); + theCurrentRenderTarget = inTarget; + theMVP = inMVP; + theContext.SetViewport(theOriginalViewport); + theDestSize = QT3DSVec2((QT3DSF32)theFinalWidth, (QT3DSF32)theFinalHeight); + // This isn't necessary if we are rendering to an offscreen buffer and not + // compositing + // with other objects. + if (inEnableBlendWhenRenderToTarget) { + theContext.SetBlendingEnabled(wasBlendingEnabled); + theContext.SetScissorTestEnabled(wasScissorEnabled); + // The blending setup was done before we apply the effect + theContext.SetBlendFunction(theBlendFunc); + theContext.SetBlendEquation(theBlendEqu); + } + } break; + case CommandTypes::BindShader: + theCurrentShader = BindShader(inEffect.m_ClassName, + static_cast<const SBindShader &>(theCommand)); + break; + case CommandTypes::ApplyInstanceValue: + if (theCurrentShader) + ApplyInstanceValue(inEffect, inClass, *theCurrentShader->m_Shader, + static_cast<const SApplyInstanceValue &>(theCommand)); + break; + case CommandTypes::ApplyValue: + if (theCurrentShader) + ApplyValue(inEffect, inClass, *theCurrentShader->m_Shader, + static_cast<const SApplyValue &>(theCommand)); + break; + case CommandTypes::ApplyBlending: + intermediateBlendingEnabled = + ApplyBlending(static_cast<const SApplyBlending &>(theCommand)); + break; + case CommandTypes::ApplyBufferValue: + if (theCurrentShader) + theCurrentSourceTexture = + ApplyBufferValue(inEffect, *theCurrentShader->m_Shader, + static_cast<const SApplyBufferValue &>(theCommand), + inSourceTexture, theCurrentSourceTexture); + break; + case CommandTypes::ApplyDepthValue: + if (theCurrentShader) + ApplyDepthValue(inEffect, *theCurrentShader->m_Shader, + static_cast<const SApplyDepthValue &>(theCommand), + inDepthTexture); + if (!inDepthTexture) { + qCCritical(INVALID_OPERATION, + "Depth value command detected but no " + "depth buffer provided for effect %s", + inEffect.m_ClassName.c_str()); + QT3DS_ASSERT(false); + } + break; + case CommandTypes::ApplyImageValue: + if (theCurrentShader) + ApplyImageValue(inEffect, *theCurrentShader->m_Shader, + static_cast<const SApplyImageValue &>(theCommand)); + break; + case CommandTypes::ApplyDataBufferValue: + if (theCurrentShader) + ApplyDataBufferValue( + inEffect, *theCurrentShader->m_Shader, + static_cast<const SApplyDataBufferValue &>(theCommand)); + break; + case CommandTypes::DepthStencil: { + const SDepthStencil &theDepthStencil = + static_cast<const SDepthStencil &>(theCommand); + theCurrentDepthStencilTexture = + FindTexture(inEffect, theDepthStencil.m_BufferName); + if (theCurrentDepthStencilTexture) + theCurrentDepthStencil = theDepthStencil; + } break; + case CommandTypes::Render: + if (theCurrentShader && theCurrentSourceTexture.m_Texture) { + RenderPass(*theCurrentShader, theMVP, theCurrentSourceTexture, + theCurrentRenderTarget, theDestSize, inCameraClipRange, + theCurrentDepthStencilTexture, theCurrentDepthStencil, + static_cast<const SRender &>(theCommand).m_DrawIndirect); + } + // Reset the source texture regardless + theCurrentSourceTexture = SEffectTextureData(&inSourceTexture, false); + theCurrentDepthStencilTexture = NULL; + theCurrentDepthStencil = Option<SDepthStencil>(); + // reset intermediate blending state + if (intermediateBlendingEnabled) { + theContext.SetBlendingEnabled(false); + intermediateBlendingEnabled = false; + } + break; + case CommandTypes::ApplyRenderState: + ApplyRenderStateValue(theCurrentRenderTarget, inDepthStencilTexture, + static_cast<const SApplyRenderState &>(theCommand)); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + SetEffectRequiresCompilation(inEffect.m_ClassName, false); + + // reset to default stencil state + if (inDepthStencilTexture) { + theContext.SetDepthStencilState(m_DefaultStencilState); + } + + // Release any per-frame buffers + if (inEffect.m_Context) { + SEffectContext &theContext(*inEffect.m_Context); + // Query for size on every loop intentional + for (QT3DSU32 idx = 0; idx < theContext.m_AllocatedBuffers.size(); ++idx) { + if (theContext.m_AllocatedBuffers[idx].m_Flags.IsSceneLifetime() == false) { + theContext.ReleaseBuffer(idx); + --idx; + } + } + for (QT3DSU32 idx = 0; idx < theContext.m_AllocatedImages.size(); ++idx) { + if (theContext.m_AllocatedImages[idx].m_Flags.IsSceneLifetime() == false) { + theContext.ReleaseImage(idx); + --idx; + } + } + } + } + } + + NVRenderTexture2D *RenderEffect(SEffectRenderArgument inRenderArgument) override + { + SEffectClass *theClass = GetEffectClass(inRenderArgument.m_Effect.m_ClassName); + if (!theClass) { + QT3DS_ASSERT(false); + return NULL; + } + QT3DSMat44 theMVP; + SCamera::SetupOrthographicCameraForOffscreenRender(inRenderArgument.m_ColorBuffer, theMVP); + // setup a render target + NVRenderContext &theContext(m_Context->GetRenderContext()); + IResourceManager &theManager(m_Context->GetResourceManager()); + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + STextureDetails theDetails(inRenderArgument.m_ColorBuffer.GetTextureDetails()); + QT3DSU32 theFinalWidth = ITextRenderer::NextMultipleOf4((QT3DSU32)(theDetails.m_Width)); + QT3DSU32 theFinalHeight = ITextRenderer::NextMultipleOf4((QT3DSU32)(theDetails.m_Height)); + NVRenderFrameBuffer *theBuffer = theManager.AllocateFrameBuffer(); + // UdoL Some Effects may need to run before HDR tonemap. This means we need to keep the + // input format + NVRenderTextureFormats::Enum theOutputFormat = NVRenderTextureFormats::RGBA8; + if (theClass->m_DynamicClass->GetOutputTextureFormat() == NVRenderTextureFormats::Unknown) + theOutputFormat = theDetails.m_Format; + NVRenderTexture2D *theTargetTexture = + theManager.AllocateTexture2D(theFinalWidth, theFinalHeight, theOutputFormat); + theBuffer->Attach(NVRenderFrameBufferAttachments::Color0, *theTargetTexture); + theContext.SetRenderTarget(theBuffer); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + NVRenderRect(0, 0, theFinalWidth, theFinalHeight)); + + NVRenderContextScopedProperty<bool> __scissorEnable( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + + DoRenderEffect(inRenderArgument.m_Effect, *theClass, inRenderArgument.m_ColorBuffer, theMVP, + m_Context->GetRenderContext().GetRenderTarget(), false, + inRenderArgument.m_DepthTexture, inRenderArgument.m_DepthStencilBuffer, + inRenderArgument.m_CameraClipRange); + + theBuffer->Attach(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer()); + theManager.Release(*theBuffer); + return theTargetTexture; + } + + // Render the effect to the currently bound render target using this MVP + bool RenderEffect(SEffectRenderArgument inRenderArgument, QT3DSMat44 &inMVP, + bool inEnableBlendWhenRenderToTarget) override + { + SEffectClass *theClass = GetEffectClass(inRenderArgument.m_Effect.m_ClassName); + if (!theClass) { + QT3DS_ASSERT(false); + return false; + } + + DoRenderEffect(inRenderArgument.m_Effect, *theClass, inRenderArgument.m_ColorBuffer, inMVP, + m_Context->GetRenderContext().GetRenderTarget(), + inEnableBlendWhenRenderToTarget, inRenderArgument.m_DepthTexture, + inRenderArgument.m_DepthStencilBuffer, inRenderArgument.m_CameraClipRange); + return true; + } + + void ReleaseEffectContext(SEffectContext *inContext) override + { + if (inContext == NULL) + return; + for (QT3DSU32 idx = 0, end = m_Contexts.size(); idx < end; ++idx) { + if (m_Contexts[idx] == inContext) { + m_Contexts.replace_with_last(idx); + NVDelete(m_Allocator, inContext); + } + } + } + + void ResetEffectFrameData(SEffectContext &inContext) override + { // Query for size on every loop intentional + for (QT3DSU32 idx = 0; idx < inContext.m_AllocatedBuffers.size(); ++idx) { + SAllocatedBufferEntry &theBuffer(inContext.m_AllocatedBuffers[idx]); + if (theBuffer.m_Flags.IsSceneLifetime() == true) + theBuffer.m_NeedsClear = true; + } + for (QT3DSU32 idx = 0; idx < inContext.m_AllocatedDataBuffers.size(); ++idx) { + SAllocatedDataBufferEntry &theDataBuffer(inContext.m_AllocatedDataBuffers[idx]); + if (theDataBuffer.m_Flags.IsSceneLifetime() == true) + theDataBuffer.m_NeedsClear = true; + } + } + + void SetShaderData(CRegisteredString path, const char8_t *data, + const char8_t *inShaderType, const char8_t *inShaderVersion, + bool inHasGeomShader, bool inIsComputeShader) override + { + m_CoreContext.GetDynamicObjectSystemCore().SetShaderData( + path, data, inShaderType, inShaderVersion, inHasGeomShader, inIsComputeShader); + } + + void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, const char8_t *inProjectDir) const override + { + ioBuffer.write((QT3DSU32)m_EffectClasses.size()); + SStringSaveRemapper theRemapper(m_Allocator, inRemapMap, inProjectDir, + m_CoreContext.GetStringTable()); + for (TEffectClassMap::const_iterator theIter = m_EffectClasses.begin(), + end = m_EffectClasses.end(); + theIter != end; ++theIter) { + const SEffectClass &theClass = *theIter->second; + CRegisteredString theClassName = theClass.m_DynamicClass->GetId(); + theClassName.Remap(inRemapMap); + ioBuffer.write(theClassName); + // Effect classes do not store any additional data from the dynamic object class. + ioBuffer.write(theClass); + } + } + + void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) override + { + m_Allocator.m_PreAllocatedBlock = inData; + m_Allocator.m_OwnsMemory = false; + qt3ds::render::SDataReader theReader(inData.begin(), inData.end()); + QT3DSU32 numEffectClasses = theReader.LoadRef<QT3DSU32>(); + SStringLoadRemapper theRemapper(m_Allocator, inStrDataBlock, inProjectDir, + m_CoreContext.GetStringTable()); + for (QT3DSU32 idx = 0, end = numEffectClasses; idx < end; ++idx) { + CRegisteredString theClassName = theReader.LoadRef<CRegisteredString>(); + theClassName.Remap(inStrDataBlock); + IDynamicObjectClass *theBaseClass = + m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(theClassName); + if (theBaseClass == NULL) { + QT3DS_ASSERT(false); + return; + } + SEffectClass *theClass = theReader.Load<SEffectClass>(); + theClass->SetupThisObjectFromMemory(m_Allocator, *theBaseClass); + NVScopedRefCounted<SEffectClass> theClassPtr(theClass); + m_EffectClasses.insert(eastl::make_pair(theBaseClass->GetId(), theClassPtr)); + } + } + + IEffectSystem &GetEffectSystem(IQt3DSRenderContext &context) override + { + m_Context = &context; + + NVRenderContext &theContext(m_Context->GetRenderContext()); + + m_ResourceManager = &IResourceManager::CreateResourceManager(theContext); + + // create default stencil state + qt3ds::render::NVRenderStencilFunctionArgument stencilDefaultFunc( + qt3ds::render::NVRenderBoolOp::AlwaysTrue, 0x0, 0xFF); + qt3ds::render::NVRenderStencilOperationArgument stencilDefaultOp( + qt3ds::render::NVRenderStencilOp::Keep, qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Keep); + m_DefaultStencilState = theContext.CreateDepthStencilState( + theContext.IsDepthTestEnabled(), theContext.IsDepthWriteEnabled(), + theContext.GetDepthFunction(), theContext.IsStencilTestEnabled(), stencilDefaultFunc, + stencilDefaultFunc, stencilDefaultOp, stencilDefaultOp); + + return *this; + } + + IResourceManager &GetResourceManager() override + { + return *m_ResourceManager; + } + + void renderSubpresentations(SEffect &inEffect) override + { + SEffectClass *theClass = GetEffectClass(inEffect.m_ClassName); + if (!theClass) + return; + + NVConstDataRef<SPropertyDefinition> theDefs = theClass->m_DynamicClass->GetProperties(); + for (QT3DSU32 idx = 0, end = theDefs.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theDefs[idx]); + if (theDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + QT3DSU8 *dataPtr = inEffect.GetDataSectionBegin() + theDefinition.m_Offset; + StaticAssert<sizeof(CRegisteredString) + == sizeof(NVRenderTexture2DPtr)>::valid_expression(); + CRegisteredString *theStrPtr = reinterpret_cast<CRegisteredString *>(dataPtr); + IOffscreenRenderManager &theOffscreenRenderer( + m_Context->GetOffscreenRenderManager()); + + if (theStrPtr->IsValid()) { + if (theOffscreenRenderer.HasOffscreenRenderer(*theStrPtr)) + theOffscreenRenderer.GetRenderedItem(*theStrPtr); + } + } + } + } +}; +} + +IEffectSystemCore &IEffectSystemCore::CreateEffectSystemCore(IQt3DSRenderContextCore &inContext) +{ + return *QT3DS_NEW(inContext.GetAllocator(), SEffectSystem)(inContext); +} diff --git a/src/runtimerender/Qt3DSRenderEffectSystem.h b/src/runtimerender/Qt3DSRenderEffectSystem.h new file mode 100644 index 0000000..879addc --- /dev/null +++ b/src/runtimerender/Qt3DSRenderEffectSystem.h @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_EFFECT_SYSTEM_H +#define QT3DS_RENDER_EFFECT_SYSTEM_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSVec2.h" +#include "Qt3DSRenderDynamicObjectSystem.h" + +namespace qt3ds { +namespace render { + struct SEffect; + struct SEffectContext; + namespace dynamic { + struct SCommand; // UICRenderEffectCommands.h + } + + struct SEffectRenderArgument + { + SEffect &m_Effect; + NVRenderTexture2D &m_ColorBuffer; + // Some effects need the camera near and far ranges. + QT3DSVec2 m_CameraClipRange; + // Some effects require the depth buffer from the rendering of thelayer + // most do not. + NVRenderTexture2D *m_DepthTexture; + // this is a depth preapass texture we need for some effects like bloom + // actually we need the stencil values + NVRenderTexture2D *m_DepthStencilBuffer; + + SEffectRenderArgument(SEffect &inEffect, NVRenderTexture2D &inColorBuffer, + const QT3DSVec2 &inCameraClipRange, + NVRenderTexture2D *inDepthTexture = NULL, + NVRenderTexture2D *inDepthBuffer = NULL) + : m_Effect(inEffect) + , m_ColorBuffer(inColorBuffer) + , m_CameraClipRange(inCameraClipRange) + , m_DepthTexture(inDepthTexture) + , m_DepthStencilBuffer(inDepthBuffer) + { + } + }; + + class IEffectSystemCore : public NVRefCounted + { + public: + virtual bool IsEffectRegistered(CRegisteredString inStr) = 0; + virtual NVConstDataRef<CRegisteredString> GetRegisteredEffects() = 0; + // Register an effect class that uses exactly these commands to render. + // Effect properties cannot change after the effect is created because that would invalidate + // existing effect instances. + // Effect commands, which are stored on the effect class, can change. + virtual bool RegisterEffect(CRegisteredString inName, + NVConstDataRef<dynamic::SPropertyDeclaration> inProperties) = 0; + + virtual bool UnregisterEffect(CRegisteredString inName) = 0; + + // Shorthand method that creates an effect and auto-generates the effect commands like such: + // BindShader(inPathToEffect) + // foreach( propdec in inProperties ) ApplyValue( propDecType ) + // ApplyShader() + virtual bool + RegisterGLSLEffect(CRegisteredString inName, const char8_t *inPathToEffect, + NVConstDataRef<dynamic::SPropertyDeclaration> inProperties) = 0; + // Set the default value. THis is unnecessary if the default is zero as that is what it is + // assumed to be. + virtual void SetEffectPropertyDefaultValue(CRegisteredString inName, + CRegisteredString inPropName, + NVConstDataRef<QT3DSU8> inDefaultData) = 0; + virtual void SetEffectPropertyEnumNames(CRegisteredString inName, + CRegisteredString inPropName, + NVConstDataRef<CRegisteredString> inNames) = 0; + virtual NVConstDataRef<CRegisteredString> + GetEffectPropertyEnumNames(CRegisteredString inName, + CRegisteredString inPropName) const = 0; + + virtual NVConstDataRef<dynamic::SPropertyDefinition> + GetEffectProperties(CRegisteredString inEffectName) const = 0; + + virtual void SetEffectPropertyTextureSettings( + CRegisteredString inEffectName, CRegisteredString inPropName, + CRegisteredString inPropPath, NVRenderTextureTypeValue::Enum inTexType, + NVRenderTextureCoordOp::Enum inCoordOp, NVRenderTextureMagnifyingOp::Enum inMagFilterOp, + NVRenderTextureMinifyingOp::Enum inMinFilterOp) = 0; + + // Setting the effect commands also sets this as if there isn't a specific "apply depth + // value" + // command then this effect does not require the depth texture. + // So the setter here is completely optional. + virtual void SetEffectRequiresDepthTexture(CRegisteredString inEffectName, + bool inValue) = 0; + virtual bool DoesEffectRequireDepthTexture(CRegisteredString inEffectName) const = 0; + + virtual void SetEffectRequiresCompilation(CRegisteredString inEffectName, + bool inValue) = 0; + virtual bool DoesEffectRequireCompilation(CRegisteredString inEffectName) const = 0; + + // The effect commands are the actual commands that run for a given effect. The tell the + // system exactly + // explicitly things like bind this shader, bind this render target, apply this property, + // run this shader + // See UICRenderEffectCommands.h for the list of commands. + // These commands are copied into the effect. + virtual void SetEffectCommands(CRegisteredString inEffectName, + NVConstDataRef<dynamic::SCommand *> inCommands) = 0; + virtual NVConstDataRef<dynamic::SCommand *> + GetEffectCommands(CRegisteredString inEffectName) const = 0; + + // Set the shader data for a given path. Used when a path doesn't correspond to a file but + // the data has been + // auto-generated. The system will look for data under this path key during the BindShader + // effect command. + virtual void SetShaderData(CRegisteredString inPath, const char8_t *inData, + const char8_t *inShaderType = NULL, + const char8_t *inShaderVersion = NULL, + bool inHasGeomShader = false, + bool inIsComputeShader = false) = 0; + + // An effect instance is just a property bag along with the name of the effect to run. + // This instance is what is placed into the object graph. + virtual SEffect *CreateEffectInstance(CRegisteredString inEffectName, + NVAllocatorCallback &inSceneGraphAllocator) = 0; + + virtual void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t *inProjectDir) const = 0; + virtual void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) = 0; + + virtual IEffectSystem &GetEffectSystem(IQt3DSRenderContext &context) = 0; + + virtual IResourceManager &GetResourceManager() = 0; + + static IEffectSystemCore &CreateEffectSystemCore(IQt3DSRenderContextCore &context); + }; + + /** + * An effect is essentially a function that takes a image and produces a new image. The source + *and dest images + * aren't guaranteed to be the same size, the effect may enlarge or shrink the result. + * A specialization is when you want the effect to render to the final render target instead of + *to a separate image. + * In this case the effect cannot enlarge or shrink the final target and it will render to the + *destination buffer + * using the given MVP. + */ + class IEffectSystem : public IEffectSystemCore + { + protected: + virtual ~IEffectSystem() {} + + public: + // Calling release effect context with no context results in no problems. + virtual void ReleaseEffectContext(SEffectContext *inEffect) = 0; + + // If the effect has a context you can call this to clear persistent buffers back to their + // original value. + virtual void ResetEffectFrameData(SEffectContext &inContext) = 0; + + // Render this effect. Returns false in the case the effect wasn't rendered and the render + // state + // is guaranteed to be the same as before. + // The texture returned is allocated using the resource manager, and it is up to the caller + // to deallocate it or return it to the temporary pool if items when necessary + // Pass in true if you want the result image premultiplied. Most of the functions in the + // system + // assume non-premultiplied color for images so probably this is false. + virtual NVRenderTexture2D *RenderEffect(SEffectRenderArgument inRenderArgument) = 0; + + // Render the effect to the currently bound render target using this MVP and optionally + // enabling blending when rendering to the target + virtual bool RenderEffect(SEffectRenderArgument inRenderArgument, QT3DSMat44 &inMVP, + bool inEnableBlendWhenRenderToTarget) = 0; + + virtual void renderSubpresentations(SEffect &inEffect) = 0; + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderEulerAngles.cpp b/src/runtimerender/Qt3DSRenderEulerAngles.cpp new file mode 100644 index 0000000..2732f03 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderEulerAngles.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +//============================================================================== +// Includes +//============================================================================== +#include <math.h> +#include <float.h> +#include <string.h> +#include <stdio.h> +#include "Qt3DSRenderEulerAngles.h" + +#ifdef _MSC_VER +#pragma warning(disable : 4365) // warnings on conversion from unsigned int to int +#endif + +//============================================================================== +// Namespace +//============================================================================== +namespace qt3ds { +namespace render { + + //============================================================================== + /** + * Constructor + */ + CEulerAngleConverter::CEulerAngleConverter() { m_OrderInfoBuffer[0] = '\0'; } + + //============================================================================== + /** + * Destructor + */ + CEulerAngleConverter::~CEulerAngleConverter() {} + + //============================================================================== + /** + * Constructs a Euler angle & holds it in a EulerAngles struct + * @param theI x rotation ( radians ) + * @param theJ y rotation ( radians ) + * @param theH z rotation ( radians ) + * @param theOrder the order this angle is in namely XYZ( static ), etc. + * use the EulOrd**** macros to generate values + * 0 to 23 is valid + * @return the euler angle + */ + EulerAngles CEulerAngleConverter::Eul_(float theI, float theJ, float theH, int theOrder) + { + EulerAngles theEulerAngle; + theEulerAngle.x = theI; + theEulerAngle.y = theJ; + theEulerAngle.z = theH; + theEulerAngle.w = (float)theOrder; + return theEulerAngle; + } + + //============================================================================== + /** + * Construct quaternion from Euler angles (in radians). + * @param theEulerAngle incoming angle( radians ) + * @return the Quaternion + */ + Quat CEulerAngleConverter::Eul_ToQuat(EulerAngles theEulerAngle) + { + Quat theQuaternion; + double a[3], ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; + int i, j, k, h, n, s, f; + + EulGetOrd((unsigned int)theEulerAngle.w, i, j, k, h, n, s, f); + if (f == EulFrmR) { + float t = theEulerAngle.x; + theEulerAngle.x = theEulerAngle.z; + theEulerAngle.z = t; + } + + if (n == EulParOdd) + theEulerAngle.y = -theEulerAngle.y; + + ti = theEulerAngle.x * 0.5; + tj = theEulerAngle.y * 0.5; + th = theEulerAngle.z * 0.5; + + ci = cos(ti); + cj = cos(tj); + ch = cos(th); + + si = sin(ti); + sj = sin(tj); + sh = sin(th); + + cc = ci * ch; + cs = ci * sh; + sc = si * ch; + ss = si * sh; + + if (s == EulRepYes) { + a[i] = cj * (cs + sc); /* Could speed up with */ + a[j] = sj * (cc + ss); /* trig identities. */ + a[k] = sj * (cs - sc); + theQuaternion.w = (float)(cj * (cc - ss)); + } else { + a[i] = cj * sc - sj * cs; + a[j] = cj * ss + sj * cc; + a[k] = cj * cs - sj * sc; + theQuaternion.w = (float)(cj * cc + sj * ss); + } + if (n == EulParOdd) + a[j] = -a[j]; + + theQuaternion.x = (float)a[X]; + theQuaternion.y = (float)a[Y]; + theQuaternion.z = (float)a[Z]; + return theQuaternion; + } + + //============================================================================== + /** + * Construct matrix from Euler angles (in radians). + * @param theEulerAngle incoming angle + * @param theMatrix outgoing matrix + */ + void CEulerAngleConverter::Eul_ToHMatrix(EulerAngles theEulerAngle, HMatrix theMatrix) + { + double ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; + int i, j, k, h, n, s, f; + EulGetOrd((unsigned int)theEulerAngle.w, i, j, k, h, n, s, f); + + if (f == EulFrmR) { + float t = theEulerAngle.x; + theEulerAngle.x = theEulerAngle.z; + theEulerAngle.z = t; + } + + if (n == EulParOdd) { + theEulerAngle.x = -theEulerAngle.x; + theEulerAngle.y = -theEulerAngle.y; + theEulerAngle.z = -theEulerAngle.z; + } + + ti = theEulerAngle.x; + tj = theEulerAngle.y; + th = theEulerAngle.z; + + ci = cos(ti); + cj = cos(tj); + ch = cos(th); + + si = sin(ti); + sj = sin(tj); + sh = sin(th); + + cc = ci * ch; + cs = ci * sh; + sc = si * ch; + ss = si * sh; + + if (s == EulRepYes) { + theMatrix[i][i] = (float)cj; + theMatrix[i][j] = (float)(sj * si); + theMatrix[i][k] = (float)(sj * ci); + theMatrix[j][i] = (float)(sj * sh); + theMatrix[j][j] = (float)(-cj * ss + cc); + theMatrix[j][k] = (float)(-cj * cs - sc); + theMatrix[k][i] = (float)(-sj * ch); + theMatrix[k][j] = (float)(cj * sc + cs); + theMatrix[k][k] = (float)(cj * cc - ss); + } else { + theMatrix[i][i] = (float)(cj * ch); + theMatrix[i][j] = (float)(sj * sc - cs); + theMatrix[i][k] = (float)(sj * cc + ss); + theMatrix[j][i] = (float)(cj * sh); + theMatrix[j][j] = (float)(sj * ss + cc); + theMatrix[j][k] = (float)(sj * cs - sc); + theMatrix[k][i] = (float)(-sj); + theMatrix[k][j] = (float)(cj * si); + theMatrix[k][k] = (float)(cj * ci); + } + + theMatrix[W][X] = 0.0; + theMatrix[W][Y] = 0.0; + theMatrix[W][Z] = 0.0; + theMatrix[X][W] = 0.0; + theMatrix[Y][W] = 0.0; + theMatrix[Z][W] = 0.0; + theMatrix[W][W] = 1.0; + } + + //============================================================================== + /** + * Convert matrix to Euler angles (in radians). + * @param theMatrix incoming matrix + * @param theOrder 0-23, use EulOrd**** to generate this value + * @return a set of angles in radians!!!! + */ + EulerAngles CEulerAngleConverter::Eul_FromHMatrix(HMatrix theMatrix, int theOrder) + { + EulerAngles theEulerAngle; + int i, j, k, h, n, s, f; + + EulGetOrd(theOrder, i, j, k, h, n, s, f); + if (s == EulRepYes) { + double sy = sqrt(theMatrix[i][j] * theMatrix[i][j] + theMatrix[i][k] * theMatrix[i][k]); + if (sy > 16 * FLT_EPSILON) { + theEulerAngle.x = (float)(atan2((double)theMatrix[i][j], (double)theMatrix[i][k])); + theEulerAngle.y = (float)(atan2((double)sy, (double)theMatrix[i][i])); + theEulerAngle.z = (float)(atan2((double)theMatrix[j][i], -(double)theMatrix[k][i])); + } else { + theEulerAngle.x = (float)(atan2(-(double)theMatrix[j][k], (double)theMatrix[j][j])); + theEulerAngle.y = (float)(atan2((double)sy, (double)theMatrix[i][i])); + theEulerAngle.z = 0; + } + } else { + double cy = sqrt(theMatrix[i][i] * theMatrix[i][i] + theMatrix[j][i] * theMatrix[j][i]); + if (cy > 16 * FLT_EPSILON) { + theEulerAngle.x = (float)(atan2((double)theMatrix[k][j], (double)theMatrix[k][k])); + theEulerAngle.y = (float)(atan2(-(double)theMatrix[k][i], (double)cy)); + theEulerAngle.z = (float)(atan2((double)theMatrix[j][i], (double)theMatrix[i][i])); + } else { + theEulerAngle.x = (float)(atan2(-(double)theMatrix[j][k], (double)theMatrix[j][j])); + theEulerAngle.y = (float)(atan2(-(double)theMatrix[k][i], (double)cy)); + theEulerAngle.z = 0; + } + } + + if (n == EulParOdd) { + theEulerAngle.x = -theEulerAngle.x; + theEulerAngle.y = -theEulerAngle.y; + theEulerAngle.z = -theEulerAngle.z; + } + + if (f == EulFrmR) { + float t = theEulerAngle.x; + theEulerAngle.x = theEulerAngle.z; + theEulerAngle.z = t; + } + theEulerAngle.w = (float)theOrder; + return theEulerAngle; + } + + //============================================================================== + /** + * Convert quaternion to Euler angles (in radians). + * @param theQuaternion incoming quaternion + * @param theOrder 0-23, use EulOrd**** to generate this value + * @return the generated angles ( radians ) + */ + EulerAngles CEulerAngleConverter::Eul_FromQuat(Quat theQuaternion, int theOrder) + { + HMatrix theMatrix; + double Nq = theQuaternion.x * theQuaternion.x + theQuaternion.y * theQuaternion.y + + theQuaternion.z * theQuaternion.z + theQuaternion.w * theQuaternion.w; + double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0; + double xs = theQuaternion.x * s; + double ys = theQuaternion.y * s; + double zs = theQuaternion.z * s; + double wx = theQuaternion.w * xs; + double wy = theQuaternion.w * ys; + double wz = theQuaternion.w * zs; + double xx = theQuaternion.x * xs; + double xy = theQuaternion.x * ys; + double xz = theQuaternion.x * zs; + double yy = theQuaternion.y * ys; + double yz = theQuaternion.y * zs; + double zz = theQuaternion.z * zs; + + theMatrix[X][X] = (float)(1.0 - (yy + zz)); + theMatrix[X][Y] = (float)(xy - wz); + theMatrix[X][Z] = (float)(xz + wy); + theMatrix[Y][X] = (float)(xy + wz); + theMatrix[Y][Y] = (float)(1.0 - (xx + zz)); + theMatrix[Y][Z] = (float)(yz - wx); + theMatrix[Z][X] = (float)(xz - wy); + theMatrix[Z][Y] = (float)(yz + wx); + theMatrix[Z][Z] = (float)(1.0 - (xx + yy)); + theMatrix[W][X] = 0.0; + theMatrix[W][Y] = 0.0; + theMatrix[W][Z] = 0.0; + theMatrix[X][W] = 0.0; + theMatrix[Y][W] = 0.0; + theMatrix[Z][W] = 0.0; + theMatrix[W][W] = 1.0; + + return Eul_FromHMatrix(theMatrix, theOrder); + } + + //============================================================================== + /** + * Dump the Order information + */ + const char *CEulerAngleConverter::DumpOrderInfo() + { + long theCount = 0; + long theOrder[24]; + char theOrderStr[24][16]; + + ::strcpy(theOrderStr[theCount++], "EulOrdXYZs"); + ::strcpy(theOrderStr[theCount++], "EulOrdXYXs"); + ::strcpy(theOrderStr[theCount++], "EulOrdXZYs"); + ::strcpy(theOrderStr[theCount++], "EulOrdXZXs"); + ::strcpy(theOrderStr[theCount++], "EulOrdYZXs"); + ::strcpy(theOrderStr[theCount++], "EulOrdYZYs"); + ::strcpy(theOrderStr[theCount++], "EulOrdYXZs"); + ::strcpy(theOrderStr[theCount++], "EulOrdYXYs"); + ::strcpy(theOrderStr[theCount++], "EulOrdZXYs"); + ::strcpy(theOrderStr[theCount++], "EulOrdZXZs"); + ::strcpy(theOrderStr[theCount++], "EulOrdZYXs"); + ::strcpy(theOrderStr[theCount++], "EulOrdZYZs"); + ::strcpy(theOrderStr[theCount++], "EulOrdZYXr"); + ::strcpy(theOrderStr[theCount++], "EulOrdXYXr"); + ::strcpy(theOrderStr[theCount++], "EulOrdYZXr"); + ::strcpy(theOrderStr[theCount++], "EulOrdXZXr"); + ::strcpy(theOrderStr[theCount++], "EulOrdXZYr"); + ::strcpy(theOrderStr[theCount++], "EulOrdYZYr"); + ::strcpy(theOrderStr[theCount++], "EulOrdZXYr"); + ::strcpy(theOrderStr[theCount++], "EulOrdYXYr"); + ::strcpy(theOrderStr[theCount++], "EulOrdYXZr"); + ::strcpy(theOrderStr[theCount++], "EulOrdZXZr"); + ::strcpy(theOrderStr[theCount++], "EulOrdXYZr"); + ::strcpy(theOrderStr[theCount++], "EulOrdZYZr"); + + theCount = 0; + theOrder[theCount++] = EulOrdXYZs; + theOrder[theCount++] = EulOrdXYXs; + theOrder[theCount++] = EulOrdXZYs; + theOrder[theCount++] = EulOrdXZXs; + theOrder[theCount++] = EulOrdYZXs; + theOrder[theCount++] = EulOrdYZYs; + theOrder[theCount++] = EulOrdYXZs; + theOrder[theCount++] = EulOrdYXYs; + theOrder[theCount++] = EulOrdZXYs; + theOrder[theCount++] = EulOrdZXZs; + theOrder[theCount++] = EulOrdZYXs; + theOrder[theCount++] = EulOrdZYZs; + + theOrder[theCount++] = EulOrdZYXr; + theOrder[theCount++] = EulOrdXYXr; + theOrder[theCount++] = EulOrdYZXr; + theOrder[theCount++] = EulOrdXZXr; + theOrder[theCount++] = EulOrdXZYr; + theOrder[theCount++] = EulOrdYZYr; + theOrder[theCount++] = EulOrdZXYr; + theOrder[theCount++] = EulOrdYXYr; + theOrder[theCount++] = EulOrdYXZr; + theOrder[theCount++] = EulOrdZXZr; + theOrder[theCount++] = EulOrdXYZr; + theOrder[theCount++] = EulOrdZYZr; + + char theSubBuf[256]; + m_OrderInfoBuffer[0] = '\0'; + for (long theIndex = 0; theIndex < 24; ++theIndex) { + ::sprintf(theSubBuf, " %16s - %ld\n ", theOrderStr[theIndex], theOrder[theIndex]); + ::strcat(m_OrderInfoBuffer, theSubBuf); + } + + return m_OrderInfoBuffer; + } +} +} // namespace Q3DStudio diff --git a/src/runtimerender/Qt3DSRenderEulerAngles.h b/src/runtimerender/Qt3DSRenderEulerAngles.h new file mode 100644 index 0000000..bf271e2 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderEulerAngles.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ + +#pragma once + +#include <qmath.h> + +namespace qt3ds { +namespace render { + //============================================================================== + // Description + //============================================================================== + // QuatTypes.h - Basic type declarations + // by Ken Shoemake, shoemake@graphics.cis.upenn.edu + // in "Graphics Gems IV", Academic Press, 1994 + typedef struct + { + float x, y, z, w; + } Quat; /* Quaternion */ + typedef float HMatrix[4][4]; /* Right-handed, for column vectors */ + enum QuatPart { X, Y, Z, W }; + typedef Quat EulerAngles; /* (x,y,z)=ang 1,2,3, w=order code */ + +#define EulFrmS 0 +#define EulFrmR 1 +#define EulFrm(ord) ((unsigned)(ord)&1) +#define EulRepNo 0 +#define EulRepYes 1 +#define EulRep(ord) (((unsigned)(ord) >> 1) & 1) +#define EulParEven 0 +#define EulParOdd 1 +#define EulPar(ord) (((unsigned)(ord) >> 2) & 1) +#define EulSafe "\000\001\002\000" +#define EulNext "\001\002\000\001" +#define EulAxI(ord) ((int)(EulSafe[(((unsigned)(ord) >> 3) & 3)])) +#define EulAxJ(ord) ((int)(EulNext[EulAxI(ord) + (EulPar(ord) == EulParOdd)])) +#define EulAxK(ord) ((int)(EulNext[EulAxI(ord) + (EulPar(ord) != EulParOdd)])) +#define EulAxH(ord) ((EulRep(ord) == EulRepNo) ? EulAxK(ord) : EulAxI(ord)) + +// EulGetOrd unpacks all useful information about order simultaneously. +#define EulGetOrd(ord, i, j, k, h, n, s, f) \ + { \ + unsigned o = ord; \ + f = o & 1; \ + o = o >> 1; \ + s = o & 1; \ + o = o >> 1; \ + n = o & 1; \ + o = o >> 1; \ + i = EulSafe[o & 3]; \ + j = EulNext[i + n]; \ + k = EulNext[i + 1 - n]; \ + h = s ? k : i; \ + } + +// EulOrd creates an order value between 0 and 23 from 4-tuple choices. +#define EulOrd(i, p, r, f) (((((((i) << 1) + (p)) << 1) + (r)) << 1) + (f)) + +// Static axes +// X = 0, Y = 1, Z = 2 ref QuatPart +#define EulOrdXYZs EulOrd(0, EulParEven, EulRepNo, EulFrmS) +#define EulOrdXYXs EulOrd(0, EulParEven, EulRepYes, EulFrmS) +#define EulOrdXZYs EulOrd(0, EulParOdd, EulRepNo, EulFrmS) +#define EulOrdXZXs EulOrd(0, EulParOdd, EulRepYes, EulFrmS) +#define EulOrdYZXs EulOrd(1, EulParEven, EulRepNo, EulFrmS) +#define EulOrdYZYs EulOrd(1, EulParEven, EulRepYes, EulFrmS) +#define EulOrdYXZs EulOrd(1, EulParOdd, EulRepNo, EulFrmS) +#define EulOrdYXYs EulOrd(1, EulParOdd, EulRepYes, EulFrmS) +#define EulOrdZXYs EulOrd(2, EulParEven, EulRepNo, EulFrmS) +#define EulOrdZXZs EulOrd(2, EulParEven, EulRepYes, EulFrmS) +#define EulOrdZYXs EulOrd(2, EulParOdd, EulRepNo, EulFrmS) +#define EulOrdZYZs EulOrd(2, EulParOdd, EulRepYes, EulFrmS) + +// Rotating axes +#define EulOrdZYXr EulOrd(0, EulParEven, EulRepNo, EulFrmR) +#define EulOrdXYXr EulOrd(0, EulParEven, EulRepYes, EulFrmR) +#define EulOrdYZXr EulOrd(0, EulParOdd, EulRepNo, EulFrmR) +#define EulOrdXZXr EulOrd(0, EulParOdd, EulRepYes, EulFrmR) +#define EulOrdXZYr EulOrd(1, EulParEven, EulRepNo, EulFrmR) +#define EulOrdYZYr EulOrd(1, EulParEven, EulRepYes, EulFrmR) +#define EulOrdZXYr EulOrd(1, EulParOdd, EulRepNo, EulFrmR) +#define EulOrdYXYr EulOrd(1, EulParOdd, EulRepYes, EulFrmR) +#define EulOrdYXZr EulOrd(2, EulParEven, EulRepNo, EulFrmR) +#define EulOrdZXZr EulOrd(2, EulParEven, EulRepYes, EulFrmR) +#define EulOrdXYZr EulOrd(2, EulParOdd, EulRepNo, EulFrmR) +#define EulOrdZYZr EulOrd(2, EulParOdd, EulRepYes, EulFrmR) + +#ifndef M_PI +#define M_PI 3.1415926535898 +#endif + +#define TODEG(x) x = (float)(x * 180 / M_PI); +#define TORAD(x) x = (float)(x / 180 * M_PI); + + class CEulerAngleConverter + { + private: + char m_OrderInfoBuffer[1024]; + + public: + CEulerAngleConverter(); + virtual ~CEulerAngleConverter(); + + public: + EulerAngles Eul_(float ai, float aj, float ah, int order); + Quat Eul_ToQuat(EulerAngles ea); + void Eul_ToHMatrix(EulerAngles ea, HMatrix M); + EulerAngles Eul_FromHMatrix(HMatrix M, int order); + EulerAngles Eul_FromQuat(Quat q, int order); + + // Debug Stuff + const char *DumpOrderInfo(); + }; +} +} // namespace Q3DStudio diff --git a/src/runtimerender/Qt3DSRenderGpuProfiler.cpp b/src/runtimerender/Qt3DSRenderGpuProfiler.cpp new file mode 100644 index 0000000..740d8a7 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderGpuProfiler.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2014 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderProfiler.h" + +#include "EASTL/string.h" +#include "EASTL/hash_map.h" + +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSFoundation.h" + +#include "Qt3DSRenderContextCore.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderTimerQuery.h" +#include "render/Qt3DSRenderSync.h" + +#define RECORDED_FRAME_DELAY 3 +#define RECORDED_FRAME_DELAY_MASK 0x0003 + +using namespace qt3ds::render; + +namespace { + +using eastl::make_pair; + +struct SGpuTimerInfo +{ + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + bool m_AbsoluteTime; + QT3DSU16 m_WriteID; + QT3DSU16 m_ReadID; + QT3DSU16 m_AverageTimeWriteID; + QT3DSU64 m_AverageTime[10]; + QT3DSU32 m_FrameID[RECORDED_FRAME_DELAY]; + NVScopedRefCounted<NVRenderTimerQuery> m_TimerStartQueryObjects[RECORDED_FRAME_DELAY]; + NVScopedRefCounted<NVRenderTimerQuery> m_TimerEndQueryObjects[RECORDED_FRAME_DELAY]; + NVScopedRefCounted<NVRenderSync> m_TimerSyncObjects[RECORDED_FRAME_DELAY]; + + SGpuTimerInfo(NVFoundationBase &inFoundation) + : m_Foundation(inFoundation) + , mRefCount(0) + , m_AbsoluteTime(false) + , m_WriteID(0) + , m_ReadID(0) + , m_AverageTimeWriteID(0) + { + memset(m_AverageTime, 0x0, 10 * sizeof(QT3DSU64)); + } + + ~SGpuTimerInfo() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void IncrementWriteCounter() + { + m_WriteID++; + m_WriteID %= RECORDED_FRAME_DELAY_MASK; + } + + void IncrementReadCounter() + { + m_ReadID++; + m_ReadID %= RECORDED_FRAME_DELAY_MASK; + } + + void IncrementAveragedWriteCounter() + { + m_AverageTimeWriteID++; + m_AverageTimeWriteID %= 10; + } + + void StartTimerQuery(QT3DSU32 frameID) + { + m_FrameID[m_WriteID] = frameID; + + if (m_AbsoluteTime) + m_TimerStartQueryObjects[m_WriteID]->SetTimerQuery(); + else + m_TimerStartQueryObjects[m_WriteID]->Begin(); + } + + void EndTimerQuery() + { + if (m_AbsoluteTime) + m_TimerEndQueryObjects[m_WriteID]->SetTimerQuery(); + else + m_TimerStartQueryObjects[m_WriteID]->End(); + + IncrementWriteCounter(); + } + + void AddSync() + { + m_TimerSyncObjects[m_WriteID]->Sync(); + m_TimerSyncObjects[m_WriteID]->Wait(); + } + + QT3DSF64 GetAveragedElapsedTimeInMs() + { + QT3DSF64 time = + QT3DSF64(((m_AverageTime[0] + m_AverageTime[1] + m_AverageTime[2] + m_AverageTime[3] + + m_AverageTime[4] + m_AverageTime[5] + m_AverageTime[6] + m_AverageTime[7] + + m_AverageTime[8] + m_AverageTime[9]) + / 10) + / 1e06); + + return time; + } + + QT3DSF64 GetElapsedTimeInMs(QT3DSU32 frameID) + { + QT3DSF64 time = 0; + + if (((frameID - m_FrameID[m_ReadID]) < 2) || (m_ReadID == m_WriteID)) + return time; + + if (m_AbsoluteTime) { + QT3DSU64 startTime, endTime; + + m_TimerStartQueryObjects[m_ReadID]->GetResult(&startTime); + m_TimerEndQueryObjects[m_ReadID]->GetResult(&endTime); + + m_AverageTime[m_AverageTimeWriteID] = endTime - startTime; + } else { + QT3DSU64 elapsedTime; + + m_TimerStartQueryObjects[m_ReadID]->GetResult(&elapsedTime); + + m_AverageTime[m_AverageTimeWriteID] = elapsedTime; + } + + IncrementReadCounter(); + IncrementAveragedWriteCounter(); + + return GetAveragedElapsedTimeInMs(); + } +}; + +class Qt3DSCRenderGpuProfiler : public IRenderProfiler +{ + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SGpuTimerInfo>> TStrGpuTimerInfoMap; + +private: + NVFoundationBase &m_Foundation; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + IQt3DSRenderContext &m_Context; + volatile QT3DSI32 mRefCount; + + TStrGpuTimerInfoMap m_StrToGpuTimerMap; + IRenderProfiler::TStrIDVec m_StrToIDVec; + mutable QT3DSU32 m_VertexCount; + +public: + Qt3DSCRenderGpuProfiler(NVFoundationBase &inFoundation, IQt3DSRenderContext &inContext, + NVRenderContext &inRenderContext) + : m_Foundation(inFoundation) + , m_RenderContext(inRenderContext) + , m_Context(inContext) + , mRefCount(0) + , m_StrToGpuTimerMap(inContext.GetAllocator(), "Qt3DSRenderGpuProfiler::m_StrToGpuTimerMap") + , m_StrToIDVec(inContext.GetAllocator(), "Qt3DSRenderGpuProfiler::m_StrToIDVec") + , m_VertexCount(0) + { + } + + virtual ~Qt3DSCRenderGpuProfiler() { m_StrToGpuTimerMap.clear(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void StartTimer(CRegisteredString &nameID, bool absoluteTime, bool sync) override + { + SGpuTimerInfo *theGpuTimerData = GetOrCreateGpuTimerInfo(nameID); + + if (theGpuTimerData) { + if (sync) + theGpuTimerData->AddSync(); + + theGpuTimerData->m_AbsoluteTime = absoluteTime; + theGpuTimerData->StartTimerQuery(m_Context.GetFrameCount()); + } + } + + void EndTimer(CRegisteredString &nameID) override + { + SGpuTimerInfo *theGpuTimerData = GetOrCreateGpuTimerInfo(nameID); + + if (theGpuTimerData) { + theGpuTimerData->EndTimerQuery(); + } + } + + QT3DSF64 GetElapsedTime(const CRegisteredString &nameID) const override + { + QT3DSF64 time = 0; + SGpuTimerInfo *theGpuTimerData = GetGpuTimerInfo(nameID); + + if (theGpuTimerData) { + time = theGpuTimerData->GetElapsedTimeInMs(m_Context.GetFrameCount()); + } + + return time; + } + + const TStrIDVec &GetTimerIDs() const override { return m_StrToIDVec; } + + void AddVertexCount(QT3DSU32 count) override { m_VertexCount += count; } + + QT3DSU32 GetAndResetTriangleCount() const override + { + QT3DSU32 tris = m_VertexCount / 3; + m_VertexCount = 0; + return tris; + } + +private: + SGpuTimerInfo *GetOrCreateGpuTimerInfo(CRegisteredString &nameID) + { + TStrGpuTimerInfoMap::const_iterator theIter = m_StrToGpuTimerMap.find(nameID); + if (theIter != m_StrToGpuTimerMap.end()) + return const_cast<SGpuTimerInfo *>(theIter->second.mPtr); + + SGpuTimerInfo *theGpuTimerData = + QT3DS_NEW(m_Context.GetAllocator(), SGpuTimerInfo)(m_Foundation); + + if (theGpuTimerData) { + // create queries + for (QT3DSU32 i = 0; i < RECORDED_FRAME_DELAY; i++) { + theGpuTimerData->m_TimerStartQueryObjects[i] = m_RenderContext->CreateTimerQuery(); + theGpuTimerData->m_TimerEndQueryObjects[i] = m_RenderContext->CreateTimerQuery(); + theGpuTimerData->m_TimerSyncObjects[i] = m_RenderContext->CreateSync(); + theGpuTimerData->m_FrameID[i] = 0; + } + m_StrToGpuTimerMap.insert(make_pair(nameID, theGpuTimerData)); + m_StrToIDVec.push_back(nameID); + } + + return theGpuTimerData; + } + + SGpuTimerInfo *GetGpuTimerInfo(const CRegisteredString &nameID) const + { + TStrGpuTimerInfoMap::const_iterator theIter = m_StrToGpuTimerMap.find(nameID); + if (theIter != m_StrToGpuTimerMap.end()) + return const_cast<SGpuTimerInfo *>(theIter->second.mPtr); + + return NULL; + } +}; +} + +IRenderProfiler &IRenderProfiler::CreateGpuProfiler(NVFoundationBase &inFnd, + IQt3DSRenderContext &inContext, + NVRenderContext &inRenderContext) +{ + return *QT3DS_NEW(inFnd.getAllocator(), Qt3DSCRenderGpuProfiler)(inFnd, inContext, inRenderContext); +} diff --git a/src/runtimerender/Qt3DSRenderGraphObjectPickQuery.h b/src/runtimerender/Qt3DSRenderGraphObjectPickQuery.h new file mode 100644 index 0000000..e63cad3 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderGraphObjectPickQuery.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_GRAPH_OBJECT_PICK_QUERY_H +#define QT3DS_RENDER_GRAPH_OBJECT_PICK_QUERY_H + +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSMat44.h" +#include "Qt3DSRenderImage.h" + +namespace qt3ds { +namespace render { + + class IOffscreenRenderer; + + struct Qt3DSRenderPickSubResult + { + IOffscreenRenderer *m_SubRenderer; + QT3DSMat44 m_TextureMatrix; + NVRenderTextureCoordOp::Enum m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum m_VerticalTilingMode; + QT3DSU32 m_ViewportWidth; + QT3DSU32 m_ViewportHeight; + Qt3DSRenderPickSubResult *m_NextSibling; + + Qt3DSRenderPickSubResult() + : m_SubRenderer(NULL) + , m_NextSibling(NULL) + { + } + Qt3DSRenderPickSubResult(IOffscreenRenderer &inSubRenderer, QT3DSMat44 inTextureMatrix, + NVRenderTextureCoordOp::Enum inHorizontalTilingMode, + NVRenderTextureCoordOp::Enum inVerticalTilingMode, QT3DSU32 width, + QT3DSU32 height) + : m_SubRenderer(&inSubRenderer) + , m_TextureMatrix(inTextureMatrix) + , m_HorizontalTilingMode(inHorizontalTilingMode) + , m_VerticalTilingMode(inVerticalTilingMode) + , m_ViewportWidth(width) + , m_ViewportHeight(height) + , m_NextSibling(NULL) + { + } + }; + + struct Qt3DSRenderPickResult + { + const SGraphObject *m_HitObject; + QT3DSF32 m_CameraDistanceSq; + // The local coordinates in X,Y UV space where the hit occured + QT3DSVec2 m_LocalUVCoords; + // The local mouse coordinates will be the same on all of the sub objects. + Qt3DSRenderPickSubResult *m_FirstSubObject; + // The offscreen renderer that was used to render the scene graph this result was produced + // from. + IOffscreenRenderer *m_OffscreenRenderer; + + Qt3DSRenderPickResult(const SGraphObject &inHitObject, QT3DSF32 inCameraDistance, + const QT3DSVec2 &inLocalUVCoords) + : m_HitObject(&inHitObject) + , m_CameraDistanceSq(inCameraDistance) + , m_LocalUVCoords(inLocalUVCoords) + , m_FirstSubObject(NULL) + , m_OffscreenRenderer(NULL) + { + } + Qt3DSRenderPickResult() + : m_HitObject(NULL) + , m_CameraDistanceSq(QT3DS_MAX_F32) + , m_LocalUVCoords(0, 0) + , m_FirstSubObject(NULL) + , m_OffscreenRenderer(NULL) + { + } + }; + + class IGraphObjectPickQuery + { + protected: + virtual ~IGraphObjectPickQuery() {} + + public: + // Implementors have the option of batching the results to allow fewer virtual calls + // or returning one item each pick. + // Results are guaranteed to be returned nearest to furthest + // If the return value has size of zero then we assume nothing more can be picked and the + // pick + // is finished. + virtual Qt3DSRenderPickResult Pick(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool inPickEverything) = 0; + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderGraphObjectSerializer.cpp b/src/runtimerender/Qt3DSRenderGraphObjectSerializer.cpp new file mode 100644 index 0000000..358c5aa --- /dev/null +++ b/src/runtimerender/Qt3DSRenderGraphObjectSerializer.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderGraphObjectSerializer.h" +#include "Qt3DSRenderPresentation.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderModel.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderEffectSystem.h" +#include "foundation/SerializationTypes.h" +#include "StringTools.h" +#include "foundation/FileTools.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderReferencedMaterial.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathSubPath.h" +#include "Qt3DSRenderPathManager.h" + +using namespace qt3ds::render; +using namespace qt3ds::render::dynamic; + +namespace { +typedef nvhash_set<void *> TPtrSet; + +void Align(MemoryBuffer<> &inBuffer) +{ + inBuffer.align(sizeof(void *)); +} +typedef nvvector<eastl::pair<GraphObjectTypes::Enum, QT3DSU32>> TObjectFileStatList; +typedef SPtrOffsetMap TPtrOffsetMap; + +struct SSerializerWriteContext +{ + SPtrOffsetMap &m_OffsetMap; + SWriteBuffer &m_MemoryBuffer; + const SStrRemapMap &m_StrRemapMap; + QT3DSU32 m_DataBlockStart; + IDynamicObjectSystem &m_DynamicObjectSystem; + IPathManager &m_PathManager; + TObjectFileStatList &m_FileSizeStats; + Qt3DSString m_PathMapper; + Qt3DSString m_BasePath; + Qt3DSString m_RelativePath; + IStringTable &m_StringTable; + SSerializerWriteContext(SPtrOffsetMap &inOffsetMap, SWriteBuffer &inWriteBuffer, + const SStrRemapMap &inStrMap, QT3DSU32 inDataBlockStart, + IDynamicObjectSystem &inDynamicObjectSystem, + IPathManager &inPathManager, TObjectFileStatList &inStats, + NVAllocatorCallback &inAllocator, const char8_t *inProjectDirectory, + IStringTable &inStringTable) + : m_OffsetMap(inOffsetMap) + , m_MemoryBuffer(inWriteBuffer) + , m_StrRemapMap(inStrMap) + , m_DataBlockStart(inDataBlockStart) + , m_DynamicObjectSystem(inDynamicObjectSystem) + , m_PathManager(inPathManager) + , m_FileSizeStats(inStats) + , m_StringTable(inStringTable) + { + Q_UNUSED(inAllocator) + m_BasePath.assign(inProjectDirectory); + } + + bool HasWrittenObject(const void *inObject) { return m_OffsetMap.contains(inObject); } + + QT3DSU32 &GetStatEntry(GraphObjectTypes::Enum inType) const + { + for (QT3DSU32 idx = 0, end = m_FileSizeStats.size(); idx < end; ++idx) + if (m_FileSizeStats[idx].first == inType) + return m_FileSizeStats[idx].second; + m_FileSizeStats.push_back(eastl::make_pair(inType, (QT3DSU32)0)); + return m_FileSizeStats.back().second; + } + + template <typename TObjType> + void AddPtrOffset(const TObjType *inObject) + { + QT3DSU32 objOffset = m_MemoryBuffer.size() - m_DataBlockStart; + m_OffsetMap.insert(eastl::make_pair(inObject, objOffset)); +// In debug we keep stats on how much each type of object +// contributes to the file size. +#ifdef _DEBUG + GetStatEntry(inObject->m_Type) += sizeof(TObjType); +#endif + } + + void Remap(CRegisteredString &inStr) { inStr.Remap(m_StrRemapMap); } + + template <typename TObjType> + void Remap(TObjType *&inPtr) + { + if (inPtr) { + TPtrOffsetMap::iterator theIter = m_OffsetMap.find(inPtr); + if (theIter != m_OffsetMap.end()) + inPtr = reinterpret_cast<TObjType *>(theIter->second); + else { + QT3DS_ASSERT(false); + } + } + } + + void RemapMaterial(SGraphObject *&inPtr) { Remap(inPtr); } + + template <typename TObjType> + void NullPtr(TObjType *&inPtr) + { + inPtr = NULL; + } +}; + +/////////////////////////////////////////////////////////////////////// +// --** Reading the scene graph is heavily threaded when we are loading +// multiple presentations in one application --** +/////////////////////////////////////////////////////////////////////// +struct SSerializerReadContext : public SDataReader +{ + IPathManagerCore &m_PathManager; + IDynamicObjectSystemCore &m_DynamicObjectSystem; + NVDataRef<QT3DSU8> m_DataBlock; + NVDataRef<QT3DSU8> m_StrTableBlock; + Qt3DSString m_PathMapper; + const char8_t *m_ProjectDirectory; + + SSerializerReadContext(IPathManagerCore &inPathManager, IDynamicObjectSystemCore &inDynSystem, + NVDataRef<QT3DSU8> inDataBlock, NVDataRef<QT3DSU8> inStrTable, + NVAllocatorCallback &inAllocator, const char8_t *inProjectDirectory) + : SDataReader(inDataBlock.begin(), inDataBlock.end()) + , m_PathManager(inPathManager) + , m_DynamicObjectSystem(inDynSystem) + , m_DataBlock(inDataBlock) + , m_StrTableBlock(inStrTable) + , m_ProjectDirectory(inProjectDirectory) + { + Q_UNUSED(inAllocator) + } + void Remap(CRegisteredString &inStr) { inStr.Remap(m_StrTableBlock); } + template <typename TObjType> + void Remap(TObjType *&inPtr) + { + if (inPtr) { + TObjType *purePtr = inPtr; + size_t ptrValue = reinterpret_cast<size_t>(purePtr); + if (ptrValue < m_DataBlock.size()) + inPtr = reinterpret_cast<TObjType *>(m_DataBlock.begin() + ptrValue); + else { + QT3DS_ASSERT(false); + inPtr = NULL; + } + } + } + void RemapMaterial(SGraphObject *&inPtr) { Remap(inPtr); } + // Nulling out pointers was done on write, so we don't do it here. + template <typename TObjType> + void NullPtr(TObjType *&) + { + } +}; + +template <typename TObjType> +struct SGraphObjectSerializerImpl +{ + static TObjType *Write(const TObjType &ioObject, SSerializerWriteContext &outSavedBuffer); + static void Remap(TObjType &inObject, SSerializerWriteContext &inRemapContext); + static TObjType *Read(SSerializerReadContext &inReadContext); +}; + +struct SWriteRemapper +{ + SSerializerWriteContext &m_WriteBuffer; + SWriteRemapper(SSerializerWriteContext &buf) + : m_WriteBuffer(buf) + { + } + // This will happen later + void Remap(const CRegisteredString &) {} + void RemapPath(const CRegisteredString &) {} + + // We ignore objects that are saved out explicitly below. + void Remap(const SScene *) {} + + void Remap(const SLayer *) {} + // Nodes are ignored because we save them out *not* in depth first order, + // with models, text, lights and camera saved out contiguously for in-memory + // traversal. + void Remap(const SNode *) {} +#ifdef _INTEGRITYPLATFORM + // explicit specialization of class "<unnamed>::SGraphObjectSerializerImpl<qt3ds::render::SCustomMaterial>" + // must precede its first use struct SGraphObjectSerializerImpl<SCustomMaterial> + template <typename TObjType> + void Remap(const TObjType *inObj); +#else + template <typename TObjType> + void Remap(const TObjType *inObj) + { + if (inObj) + SGraphObjectSerializerImpl<TObjType>::Write(*inObj, m_WriteBuffer); + } +#endif + + void RemapMaterial(const SGraphObject *inObj) + { + if (inObj) { + if (inObj->m_Type == GraphObjectTypes::DefaultMaterial) + Remap(static_cast<const SDefaultMaterial *>(inObj)); + else if (inObj->m_Type == GraphObjectTypes::CustomMaterial) + Remap(static_cast<const SCustomMaterial *>(inObj)); + else if (inObj->m_Type == GraphObjectTypes::ReferencedMaterial) + Remap(static_cast<const SReferencedMaterial *>(inObj)); + else { + QT3DS_ASSERT(false); + } + } + } + template <typename TObjType> + void NullPtr(const TObjType *) + { + } +}; + +void PrepareFirstPass(const SNode &inNode, nvvector<const SNode *> &ioLightCameras, + nvvector<const SNode *> &ioRenderable) +{ + if (GraphObjectTypes::IsRenderableType(inNode.m_Type)) + ioRenderable.push_back(&inNode); + else if (GraphObjectTypes::IsLightCameraType(inNode.m_Type)) + ioLightCameras.push_back(&inNode); + + for (const SNode *theChild = inNode.m_FirstChild; theChild; theChild = theChild->m_NextSibling) + PrepareFirstPass(*theChild, ioLightCameras, ioRenderable); +} + +template <typename TObject> +TObject *WriteGenericGraphObjectNoRemap(const TObject &ioObject, + SSerializerWriteContext &outSavedBuffer) +{ + if (outSavedBuffer.HasWrittenObject(&ioObject)) + return NULL; + + outSavedBuffer.AddPtrOffset(&ioObject); + QT3DSU32 theOffset = outSavedBuffer.m_MemoryBuffer.size(); + outSavedBuffer.m_MemoryBuffer.write(ioObject); + // Probably the buffer stays aligned but we want to work to keep it that way. + Align(outSavedBuffer.m_MemoryBuffer); + return reinterpret_cast<TObject *>(outSavedBuffer.m_MemoryBuffer.begin() + theOffset); +} + +template <typename TObject> +TObject *WriteGenericGraphObject(const TObject &ioObject, SSerializerWriteContext &outSavedBuffer) +{ + TObject *theObject = WriteGenericGraphObjectNoRemap(ioObject, outSavedBuffer); + if (theObject) // The object may have already been written. + { + // Write mappers just follow pointers and ensure all the associated objects + // are written out. + SWriteRemapper theWriteRemapper(outSavedBuffer); + const_cast<TObject &>(ioObject).Remap(theWriteRemapper); + } + return theObject; +} + +template <typename TObject> +TObject *ReadGenericGraphObject(SSerializerReadContext &inReadContext) +{ + TObject *retval = inReadContext.Load<TObject>(); + inReadContext.Align(); + if (retval) { + retval->Remap(inReadContext); + } + return retval; +} + +template <typename TObjType> +TObjType *SGraphObjectSerializerImpl<TObjType>::Write(const TObjType &ioObject, + SSerializerWriteContext &outSavedBuffer) +{ + return WriteGenericGraphObject(ioObject, outSavedBuffer); +} +template <typename TObjType> +void SGraphObjectSerializerImpl<TObjType>::Remap(TObjType &ioObject, + SSerializerWriteContext &inRemapContext) +{ + return ioObject.Remap(inRemapContext); +} + +template <typename TObjType> +TObjType *SGraphObjectSerializerImpl<TObjType>::Read(SSerializerReadContext &inReadContext) +{ + return ReadGenericGraphObject<TObjType>(inReadContext); +} + +void RemapProperties(SDynamicObject &ioObject, SSerializerWriteContext &outSavedBuffer, + CRegisteredString inClassName) +{ + NVConstDataRef<SPropertyDefinition> theObjectProps = + outSavedBuffer.m_DynamicObjectSystem.GetProperties(inClassName); + for (QT3DSU32 idx = 0, end = theObjectProps.size(); idx < end; ++idx) { + const SPropertyDefinition &theDef(theObjectProps[idx]); + if (theDef.m_DataType == qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + CRegisteredString *theStr = reinterpret_cast<CRegisteredString *>( + ioObject.GetDataSectionBegin() + theDef.m_Offset); + outSavedBuffer.Remap(*theStr); + } + } +} + +void RemapProperties(SDynamicObject &ioObject, SSerializerReadContext &inReadContext) +{ + // CN - !!Note this call is done on multiple threads simultaneously. I added a mutex just to be + // sure even though + // this is a read-only call; I am not certain how good the arm memory locking is when it is + // completely unprotected. + NVConstDataRef<SPropertyDefinition> theProperties = + inReadContext.m_DynamicObjectSystem.GetProperties(ioObject.m_ClassName); + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theProperties[idx]); + if (theDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + CRegisteredString *theString = reinterpret_cast<CRegisteredString *>( + ioObject.GetDataSectionBegin() + theDefinition.m_Offset); + inReadContext.Remap(*theString); + } + } +} + +template <> +struct SGraphObjectSerializerImpl<SEffect> +{ + static SGraphObject *Write(const SEffect &ioObject, SSerializerWriteContext &outSavedBuffer) + { + size_t itemOffset = outSavedBuffer.m_MemoryBuffer.size(); + SEffect *theNewEffect = + static_cast<SEffect *>(WriteGenericGraphObjectNoRemap(ioObject, outSavedBuffer)); + if (theNewEffect) { + theNewEffect->m_Context = NULL; + // Writing it out is easy. Reading it back in means we have to have a correctly setup + // IEffectManager so we + // can remap strings. + outSavedBuffer.m_MemoryBuffer.write(ioObject.GetDataSectionBegin(), + ioObject.m_DataSectionByteSize); + Align(outSavedBuffer.m_MemoryBuffer); + SWriteRemapper theWriteRemapper(outSavedBuffer); + // Write any connected objects. + theNewEffect = + reinterpret_cast<SEffect *>(outSavedBuffer.m_MemoryBuffer.begin() + itemOffset); + theNewEffect->Remap(theWriteRemapper); + } + return theNewEffect; + } + + static void Remap(SEffect &ioObject, SSerializerWriteContext &outSavedBuffer) + { + CRegisteredString theClassName = ioObject.m_ClassName; + ioObject.Remap(outSavedBuffer); + RemapProperties(ioObject, outSavedBuffer, theClassName); + } + + static SEffect *Read(SSerializerReadContext &inReadContext) + { + SEffect *theEffect = ReadGenericGraphObject<SEffect>(inReadContext); + if (theEffect) { + inReadContext.m_CurrentPtr += theEffect->m_DataSectionByteSize; + inReadContext.Align(); + RemapProperties(*theEffect, inReadContext); + } + return theEffect; + } +}; + +template <> +struct SGraphObjectSerializerImpl<SCustomMaterial> +{ + static SGraphObject *Write(const SCustomMaterial &ioObject, + SSerializerWriteContext &outSavedBuffer) + { + size_t itemOffset = outSavedBuffer.m_MemoryBuffer.size(); + SCustomMaterial *theNewObject = static_cast<SCustomMaterial *>( + WriteGenericGraphObjectNoRemap(ioObject, outSavedBuffer)); + if (theNewObject) { + // Writing it out is easy. Reading it back in means we have to have a correctly setup + // IEffectManager so we + // can remap strings. + outSavedBuffer.m_MemoryBuffer.write(ioObject.GetDataSectionBegin(), + ioObject.m_DataSectionByteSize); + Align(outSavedBuffer.m_MemoryBuffer); + theNewObject = reinterpret_cast<SCustomMaterial *>(outSavedBuffer.m_MemoryBuffer.begin() + + itemOffset); + SWriteRemapper theWriteRemapper(outSavedBuffer); + // Write any connected objects. + theNewObject->Remap(theWriteRemapper); + } + return theNewObject; + } + + static void Remap(SCustomMaterial &ioObject, SSerializerWriteContext &outSavedBuffer) + { + CRegisteredString theClassName(ioObject.m_ClassName); + ioObject.Remap(outSavedBuffer); + RemapProperties(ioObject, outSavedBuffer, theClassName); + } + + static SCustomMaterial *Read(SSerializerReadContext &inReadContext) + { + SCustomMaterial *theMaterial = ReadGenericGraphObject<SCustomMaterial>(inReadContext); + if (theMaterial) { + inReadContext.m_CurrentPtr += theMaterial->m_DataSectionByteSize; + inReadContext.Align(); + RemapProperties(*theMaterial, inReadContext); + } + return theMaterial; + } +}; + +#ifdef _INTEGRITYPLATFORM + template <typename TObjType> + void SWriteRemapper::Remap(const TObjType *inObj) + { + if (inObj) + SGraphObjectSerializerImpl<TObjType>::Write(*inObj, m_WriteBuffer); + } +#endif + +template <> +struct SGraphObjectSerializerImpl<SPathSubPath> +{ + static SGraphObject *Write(const SPathSubPath &ioObject, + SSerializerWriteContext &outSavedBuffer) + { + SPathSubPath *theObject = WriteGenericGraphObjectNoRemap(ioObject, outSavedBuffer); + if (theObject) // The object may have already been written. + { + NVConstDataRef<SPathAnchorPoint> thePoints = + outSavedBuffer.m_PathManager.GetPathSubPathBuffer(ioObject); + outSavedBuffer.m_MemoryBuffer.write((QT3DSU32)thePoints.size()); + outSavedBuffer.m_MemoryBuffer.write(thePoints.begin(), thePoints.size()); + // Write mappers just follow pointers and ensure all the associated objects + // are written out. + SWriteRemapper theWriteRemapper(outSavedBuffer); + const_cast<SPathSubPath &>(ioObject).Remap(theWriteRemapper); + } + return theObject; + } + + static void Remap(SPathSubPath &ioObject, SSerializerWriteContext &outSavedBuffer) + { + ioObject.Remap(outSavedBuffer); + } + + static SPathSubPath *Read(SSerializerReadContext &inReadContext) + { + SPathSubPath *theSubPath = ReadGenericGraphObject<SPathSubPath>(inReadContext); + if (theSubPath) { + QT3DSU32 numPoints = *inReadContext.Load<QT3DSU32>(); + SPathAnchorPoint *theAnchorPointBuffer = + reinterpret_cast<qt3ds::render::SPathAnchorPoint *>(inReadContext.m_CurrentPtr); + inReadContext.m_CurrentPtr += sizeof(SPathAnchorPoint) * numPoints; + + // CN - !!Note this call is done on multiple threads simultaneously. I added a mutex to + // the path manager object + // so this exact call is always protected. This absolutely caused crashing when it was + // not protected approriately. + inReadContext.m_PathManager.SetPathSubPathData( + *theSubPath, toConstDataRef(theAnchorPointBuffer, numPoints)); + } + return theSubPath; + } +}; + +void WriteGraphObject(const SGraphObject &inObject, SSerializerWriteContext &outSavedBuffer) +{ + SGraphObject *newObject = NULL; + switch (inObject.m_Type) { +#define QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(type) \ + case GraphObjectTypes::type: \ + newObject = SGraphObjectSerializerImpl<S##type>::Write( \ + static_cast<const S##type &>(inObject), outSavedBuffer); \ + break; + QT3DS_RENDER_ITERATE_GRAPH_OBJECT_TYPES +#undef QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE + default: + QT3DS_ASSERT(false); + break; + } +} + +void WriteNodeList(nvvector<const SNode *> &inList, SSerializerWriteContext &outSavedBuffer) +{ + for (QT3DSU32 idx = 0, end = inList.size(); idx < end; ++idx) + WriteGraphObject(*inList[idx], outSavedBuffer); +} + +// Now write everything you haven't written so far, skip writing renderables or cameras or lights +void WriteNonRenderableNonCLNode(const SNode &inNode, SSerializerWriteContext &outSavedBuffer) +{ + if (GraphObjectTypes::IsLightCameraType(inNode.m_Type) == false + && GraphObjectTypes::IsRenderableType(inNode.m_Type) == false) { + WriteGraphObject(inNode, outSavedBuffer); + } + for (const SNode *theChild = inNode.m_FirstChild; theChild; theChild = theChild->m_NextSibling) + WriteNonRenderableNonCLNode(*theChild, outSavedBuffer); +} + +SGraphObject *ReadGraphObject(SSerializerReadContext &inContext) +{ + if (inContext.m_CurrentPtr + sizeof(SGraphObject) < inContext.m_EndPtr) { + SGraphObject *theObject = reinterpret_cast<SGraphObject *>(inContext.m_CurrentPtr); + switch (theObject->m_Type) { +#define QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(type) \ + case GraphObjectTypes::type: \ + SGraphObjectSerializerImpl<S##type>::Read(inContext); \ + break; + QT3DS_RENDER_ITERATE_GRAPH_OBJECT_TYPES +#undef QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE + default: + QT3DS_ASSERT(false); + theObject = NULL; + break; + } + return theObject; + } + return NULL; +} +} + +void SGraphObjectSerializer::Save(NVFoundationBase &inFoundation, + const SPresentation &inPresentation, + qt3ds::render::SWriteBuffer &outSavedData, + IDynamicObjectSystem &inDynamicObjectSystem, + IPathManager &inPathManager, SPtrOffsetMap &outSceneGraphOffsets, + IStringTable &inStringTable, + NVDataRef<SGraphObject *> inExtraGraphObjects) +{ + using namespace qt3ds::foundation; + nvvector<const SNode *> theLightCameraList(inFoundation.getAllocator(), + "SGraphObjectSerializer::theLightCameraList"); + nvvector<const SNode *> theRenderableList(inFoundation.getAllocator(), + "SGraphObjectSerializer::theRenderableList"); + TObjectFileStatList theStatList(inFoundation.getAllocator(), + "SGraphObjectSerializer::FileSizeStats"); + // We want to save out the scene graph in the order we are going to traverse it normally. + // This is reverse depth first for the lights, cameras, and renderables and depth first for + // everything else so we go + // in two passes per layer. + // We expect the incoming data buffer to be aligned already. + QT3DS_ASSERT(outSavedData.size() % 4 == 0); + if (inPresentation.m_Scene) { + QT3DSU32 theDataSectionStart = outSavedData.size(); + outSavedData.writeZeros(4); + SSerializerWriteContext theWriteContext( + outSceneGraphOffsets, outSavedData, inStringTable.GetRemapMap(), theDataSectionStart, + inDynamicObjectSystem, inPathManager, theStatList, inFoundation.getAllocator(), + inPresentation.m_PresentationDirectory, inStringTable); + // First pass, just write out the data. + WriteGraphObject(inPresentation, theWriteContext); + WriteGraphObject(*inPresentation.m_Scene, theWriteContext); + for (const SLayer *theLayer = inPresentation.m_Scene->m_FirstChild; theLayer; + theLayer = static_cast<const SLayer *>(theLayer->m_NextSibling)) { + theLightCameraList.clear(); + theRenderableList.clear(); + PrepareFirstPass(*theLayer, theLightCameraList, theRenderableList); + eastl::reverse(theLightCameraList.begin(), theLightCameraList.end()); + eastl::reverse(theRenderableList.begin(), theRenderableList.end()); + WriteNodeList(theLightCameraList, theWriteContext); + WriteNodeList(theRenderableList, theWriteContext); + } + // Now just write everything *but* renderable objects and cameras. + for (const SLayer *theLayer = inPresentation.m_Scene->m_FirstChild; theLayer; + theLayer = static_cast<const SLayer *>(theLayer->m_NextSibling)) { + WriteNonRenderableNonCLNode(*theLayer, theWriteContext); + } + // Write out any extra objects we haven't covered yet. + for (QT3DSU32 idx = 0, end = inExtraGraphObjects.size(); idx < end; ++idx) + WriteGraphObject(*inExtraGraphObjects[idx], theWriteContext); + + QT3DSU32 theNumObjects = theWriteContext.m_OffsetMap.size(); + QT3DSU32 *theCountPtr = reinterpret_cast<QT3DSU32 *>(outSavedData.begin() + theDataSectionStart); + *theCountPtr = theNumObjects; + + // Second pass, perform remapping on all the objects to change their pointers to offsets + for (SPtrOffsetMap::iterator theIter = outSceneGraphOffsets.begin(), + theEnd = outSceneGraphOffsets.end(); + theIter != theEnd; ++theIter) { + QT3DSU8 *theDataPtr = outSavedData.begin() + theDataSectionStart + theIter->second; + SGraphObject *theGraphObj = reinterpret_cast<SGraphObject *>(theDataPtr); + switch (theGraphObj->m_Type) { +#define QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(type) \ + case GraphObjectTypes::type: \ + SGraphObjectSerializerImpl<S##type>::Remap(static_cast<S##type &>(*theGraphObj), \ + theWriteContext); \ + break; + QT3DS_RENDER_ITERATE_GRAPH_OBJECT_TYPES +#undef QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE + default: + QT3DS_ASSERT(false); + break; + } + } + } +#ifdef _DEBUG + qCDebug(TRACE_INFO, "--File size stats:"); + // Tell the users how much space is used in the file based on object type: + for (QT3DSU32 idx = 0, end = theStatList.size(); idx < end; ++idx) { + const char *theObjType = GraphObjectTypes::GetObjectTypeName(theStatList[idx].first); + qCDebug(TRACE_INFO, "%s - %d bytes:", theObjType, theStatList[idx].second); + } + qCDebug(TRACE_INFO, "--End file size stats:"); +#endif +}; + +SPresentation *SGraphObjectSerializer::Load(NVDataRef<QT3DSU8> inData, NVDataRef<QT3DSU8> inStrDataBlock, + IDynamicObjectSystemCore &inDynamicObjectSystem, + IPathManagerCore &inPathManager, + NVAllocatorCallback &inAllocator, + const char8_t *inProjectDirectory) +{ + SSerializerReadContext theReadContext(inPathManager, inDynamicObjectSystem, inData, + inStrDataBlock, inAllocator, inProjectDirectory); + SPresentation *retval = NULL; + if (inData.size() < 4) { + QT3DS_ASSERT(false); + return NULL; + } + QT3DSU32 theNumObjects = theReadContext.LoadRef<QT3DSU32>(); + for (QT3DSU32 idx = 0, end = theNumObjects; idx < end; ++idx) { + SGraphObject *theObject = ReadGraphObject(theReadContext); + if (theObject) { + if (theObject->m_Type == GraphObjectTypes::Presentation) + retval = static_cast<SPresentation *>(theObject); + } else { + QT3DS_ASSERT(false); + } + } + return retval; +} diff --git a/src/runtimerender/Qt3DSRenderGraphObjectSerializer.h b/src/runtimerender/Qt3DSRenderGraphObjectSerializer.h new file mode 100644 index 0000000..8f8420c --- /dev/null +++ b/src/runtimerender/Qt3DSRenderGraphObjectSerializer.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_GRAPH_OBJECT_SERIALZER_H +#define QT3DS_RENDER_GRAPH_OBJECT_SERIALZER_H + +#include "Qt3DSRender.h" +#include "foundation/Qt3DSDataRef.h" + +namespace qt3ds { +class NVFoundationBase; +} + +namespace qt3ds { +namespace render { + struct SPresentation; + class IEffectSystem; + + struct SGraphObjectSerializer + { + // This will save the tree as it exists but clients may wish to save out extra objects in + // addtion + static void + Save(NVFoundationBase &inFoundation, const SPresentation &inPresentation, + SWriteBuffer &outSavedData, IDynamicObjectSystem &inDynamicObjectSystem, + IPathManager &inPathManager, SPtrOffsetMap &outSceneGraphOffsets, + IStringTable &inStringTable, + NVDataRef<SGraphObject *> inExtraGraphObjects = NVDataRef<SGraphObject *>()); + + // Loading requires a correctly setup effect system because the effects have arbitrary data + // and the strings embedded in that data will + // require string remapping. + static SPresentation *Load(NVDataRef<QT3DSU8> inData, NVDataRef<QT3DSU8> inStrDataBlock, + IDynamicObjectSystemCore &inDynamicObjectSystem, + IPathManagerCore &inPathManager, + NVAllocatorCallback &inAllocator, + const char8_t *inProjectDirectory); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderGraphObjectTypes.h b/src/runtimerender/Qt3DSRenderGraphObjectTypes.h new file mode 100644 index 0000000..620f6c5 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderGraphObjectTypes.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_GRAPH_OBJECT_TYPES_H +#define QT3DS_RENDER_GRAPH_OBJECT_TYPES_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSAssert.h" + +namespace qt3ds { +namespace render { + +// If you need a generic switch statement, then these macros will ensure +// you get all the types the first time. +#define QT3DS_RENDER_ITERATE_GRAPH_OBJECT_TYPES \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Presentation) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Scene) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Node) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Layer) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Light) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Camera) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Model) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(DefaultMaterial) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Image) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Text) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Effect) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(RenderPlugin) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(CustomMaterial) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(ReferencedMaterial) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(Path) \ + QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(PathSubPath) + + struct GraphObjectTypes + { + enum Enum { + Unknown = 0, + Presentation, + Scene, + Node, + Layer, + Light, + Camera, + Model, + DefaultMaterial, + Image, + Text, + Effect, + CustomMaterial, + RenderPlugin, + ReferencedMaterial, + Path, + PathSubPath, + Lightmaps, + LastKnownGraphObjectType, + }; + + static bool IsMaterialType(Enum type) + { + switch (type) { + case ReferencedMaterial: + case CustomMaterial: + case DefaultMaterial: + return true; + default: + return false; + } + } + + static bool IsLightmapType(Enum type) + { + switch (type) { + case Lightmaps: + case DefaultMaterial: + return true; + default: + return false; + } + } + + static bool IsNodeType(Enum type) + { + switch (type) { + case Node: + case Layer: + case Light: + case Camera: + case Model: + case Text: + case Path: + return true; + + default: + break; + } + return false; + } + + static bool IsRenderableType(Enum type) + { + switch (type) { + case Model: + case Text: + case Path: + return true; + default: + break; + } + return false; + } + + static bool IsLightCameraType(Enum type) + { + switch (type) { + case Camera: + case Light: + return true; + default: + break; + } + return false; + } + static const char *GetObjectTypeName(Enum inType) + { + switch (inType) { +#define QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE(type) \ + case type: \ + return #type; + QT3DS_RENDER_ITERATE_GRAPH_OBJECT_TYPES +#undef QT3DS_RENDER_HANDL_GRAPH_OBJECT_TYPE + default: + break; + } + QT3DS_ASSERT(false); + return ""; + } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderImageScaler.cpp b/src/runtimerender/Qt3DSRenderImageScaler.cpp new file mode 100644 index 0000000..d699179 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderImageScaler.cpp @@ -0,0 +1,883 @@ +/**************************************************************************** +** +** Copyright (C) 1999-2001 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ + +//============================================================================== +// Includes +//============================================================================== +#include "Qt3DSRender.h" +#include "foundation/Qt3DSMath.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "Qt3DSRenderImageScaler.h" +#include "foundation/Qt3DSMemoryBuffer.h" + +using namespace qt3ds::render; +//============================================================================== +// Namespace +//============================================================================== +CImageScaler::CImageScaler(NVAllocatorCallback &inAlloc) + : m_Allocator(inAlloc) +{ +} +//============================================================================== +/** + * Scales the given image by the given scale factor. + * + * This method creates a new image based on the parameters given. + * + * @param inScaleMethod type of scaling operation + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. + */ +void CImageScaler::Scale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer, + unsigned long inOldWidth, unsigned long inOldHeight, + unsigned char *&outNewBuffer, unsigned long inNewWidth, + unsigned long inNewHeight, unsigned long inPlanes) +{ + switch (inScaleMethod) { + case SCALEMETHOD_CROP: + CImageScaler::Crop(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth, + inNewHeight, inPlanes); + break; + + case SCALEMETHOD_BILINEAR: + CImageScaler::Bilinear(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth, + inNewHeight, inPlanes); + break; + + default: + QT3DS_ASSERT(false); + break; + } +} + +//============================================================================== +/** + * Scales the given image by the given scale factor. + * + * This method creates a new image based on the parameters given. + * + * @param inScaleMethod type of scaling operation + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. + */ +void CImageScaler::FastScale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer, + unsigned long inOldWidth, unsigned long inOldHeight, + unsigned char *&outNewBuffer, unsigned long inNewWidth, + unsigned long inNewHeight, unsigned long inPlanes) +{ + switch (inScaleMethod) { + case SCALEMETHOD_CROP: + CImageScaler::Crop(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth, + inNewHeight, inPlanes); + break; + + case SCALEMETHOD_POINTSAMPLE: + CImageScaler::FastPointSample(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, + inNewWidth, inNewHeight, inPlanes); + break; + + default: + QT3DS_ASSERT(false); + break; + } +} + +//============================================================================== +/** + * Debug method that simply crops the picture instead of scaling. + * + * Not for use in production. This is a test method to exercise the framework. + * + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. +*/ +void CImageScaler::Crop(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, unsigned long inPlanes) +{ + Q_UNUSED(inOldHeight); + + QT3DS_ASSERT(inNewWidth <= inOldWidth); + QT3DS_ASSERT(inNewHeight <= inOldHeight); + + long theMinWidth = NVMin(inOldWidth, inNewWidth); + + outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes]; + ::memset(outNewBuffer, 0, inNewWidth * inNewHeight * inPlanes); + + for (unsigned long theRow = 0; theRow < inNewHeight; ++theRow) { + ::memcpy(outNewBuffer + theRow * inNewWidth * inPlanes, + inOldBuffer + theRow * inOldWidth * inPlanes, theMinWidth * inPlanes); + } +} + +//============================================================================== +/** + * Plain scaling. + * + * Code adopted from http://www.codeguru.com/bitmap/SmoothBitmapResizing.html + * Preliminary formatting completed but still needs work. + * + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. +*/ +void CImageScaler::Bilinear(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, + unsigned long inPlanes) +{ + QT3DS_ASSERT(inPlanes > 0); + + outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes]; + CImageScaler::Resize(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth, + inNewHeight, inPlanes); +} + +//============================================================================== +/** + * Fast removal of selected pixels. + * + * Really fast scanning of every n-th pixel. This algorithm works basically by + * adding a fraction to the source pointer for each pixel destination, using + * fixed point arithmetic. + * + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. +*/ +void CImageScaler::FastPointSample(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, + unsigned long inPlanes) +{ + unsigned long theXAccum; + unsigned long theYAccum; + unsigned long theXConst; + unsigned long theYConst; + unsigned long theRow; + unsigned long theColumn; + unsigned long theAdd; + unsigned long theSrcIndex; + outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes]; + + // *** Debug *** + // char dMessage[100]; + //::sprintf( dMessage, "PointSample: %ldx%ld to %ldx%ld\n",inOldInfo.m_Width, inOldInfo.m_Height, outNewInfo.m_Width, outNewInfo.m_Height ); + //::OutputDebugString( dMessage ); + + switch (inPlanes) { + case 4: { + long *theSrc; + long *theDst; + theSrc = reinterpret_cast<long *>(inOldBuffer); + theDst = reinterpret_cast<long *>(outNewBuffer); + theYAccum = 0; + theXConst = (inOldWidth << 16) / inNewWidth; + theYConst = (inOldHeight << 16) / inNewHeight; + unsigned long theAdd; + for (theRow = 0; theRow < inNewHeight; theRow++) { + theXAccum = 0; + theSrcIndex = 0; + for (theColumn = 0; theColumn < inNewWidth; theColumn++) { + + theXAccum += theXConst; + theAdd = theXAccum >> 16; + *theDst = theSrc[theSrcIndex]; + theDst++; + theSrcIndex += theAdd; + // Clear out the integer portion of the accumulator. + theXAccum = theXAccum & 0xFFFF; + } + + theYAccum += theYConst; + theAdd = (theYAccum) >> 16; + theSrc += theAdd * inOldWidth; + // Clear out the integer portion of the accumulator. + theYAccum = theYAccum & 0xFFFF; + } + } break; + + case 3: { + unsigned char *theDest; + unsigned char *theSource; + theDest = reinterpret_cast<unsigned char *>(outNewBuffer); + theSource = reinterpret_cast<unsigned char *>(inOldBuffer); + theYAccum = 0; + theXConst = (inOldWidth << 16) / inNewWidth; + theYConst = (inOldHeight << 16) / inNewHeight; + for (theRow = 0; theRow < inNewHeight; ++theRow) { + theXAccum = 0; + theSrcIndex = 0; + for (theColumn = 0; theColumn < inNewWidth; ++theColumn) { + theDest[0] = theSource[0]; + theDest[1] = theSource[1]; + theDest[2] = theSource[2]; + theDest += 3; + theSrcIndex += 3 * (theXAccum) >> 16; + theXAccum = theXAccum & 0xFFFF; + } + theYAccum += theYConst; + theAdd = (theYAccum) >> 16; + theSource += theAdd * inOldWidth * 3; + theYAccum = theYAccum & 0xFFFF; + } + } break; + + case 2: { + short *theDest; + short *theSource; + theDest = reinterpret_cast<short *>(outNewBuffer); + theSource = reinterpret_cast<short *>(inOldBuffer); + theYAccum = 0; + theXConst = (inOldWidth << 16) / inNewWidth; + theYConst = (inOldHeight << 16) / inNewHeight; + for (unsigned long theY = 0; theY < inNewHeight; ++theY) { + theXAccum = 0; + theSrcIndex = 0; + for (unsigned long theX = 0; theX < inNewWidth; ++theX) { + *theDest = *theSource; + ++theDest; + theXAccum += theXConst; + theSrcIndex += (theXAccum) >> 16; + theXAccum = theXAccum & 0xFFFF; + } + theYAccum += theYConst; + theAdd = (theYAccum) >> 16; + theSource += theAdd * inOldWidth; + theYAccum = theYAccum & 0xFFFF; + } + } break; + + case 1: { + unsigned char *theDest; + unsigned char *theSource; + theDest = reinterpret_cast<unsigned char *>(outNewBuffer); + theSource = reinterpret_cast<unsigned char *>(inOldBuffer); + theYAccum = 0; + theXConst = (inOldWidth << 16) / inNewWidth; + theYConst = (inOldHeight << 16) / inNewHeight; + for (unsigned long theY = 0; theY < inNewHeight; ++theY) { + theXAccum = 0; + theSrcIndex = 0; + for (unsigned long theX = 0; theX < inNewWidth; ++theX) { + *theDest = *theSource; + ++theDest; + theXAccum += theXConst; + theSrcIndex += (theXAccum) >> 16; + theXAccum = theXAccum & 0xFFFF; + } + theYAccum += theYConst; + theAdd = (theYAccum) >> 16; + theSource += theAdd * inOldWidth; + theYAccum = theYAccum & 0xFFFF; + } + } break; + + default: + QT3DS_ASSERT(false); + break; + } +} + +//============================================================================== +/** + * @param inWidth + * @param inHeight + * @return unsigned char* + */ +unsigned char *CImageScaler::AllocateBuffer(long inWidth, long inHeight) +{ + unsigned char *theBuffer = new unsigned char[inHeight * inWidth * 4]; + return theBuffer; +} + +//============================================================================== +/** + * @param ioBuffer the buffer to release + */ +void CImageScaler::ReleaseBuffer(unsigned char *&ioBuffer) +{ + delete[] ioBuffer; + ioBuffer = NULL; +} + +//============================================================================== +/** + * @param inOldBuffer points to the old picture + * @param inOldWidth width of the old picture + * @param inOldHeight height of the old picture + * @param inNewBuffer will point to the scaled picture + * @param inNewWidth width of the new picture + * @param inNewHeight height of the new picture + * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc) + * also equivalent to the return value of the CTextureType::PixelSize method. + */ +void CImageScaler::Resize(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, + unsigned long inPlanes) +{ + QT3DS_ASSERT(inPlanes == 4); + + // only do the temporary allocation if necessary + if (inOldWidth < inNewWidth || inOldHeight < inNewHeight) { + CImageScaler::ExpandRowsAndColumns(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, + inNewWidth, inNewHeight, inPlanes); + return; + } else { + // The downsampling algorithms *do* assume four planes. + if (inOldWidth > inNewWidth && inOldHeight > inNewHeight) { + MemoryBuffer<> theBuffer(ForwardingAllocator(m_Allocator, "ImageScaler::TempBuffer")); + theBuffer.reserve(inNewWidth * inOldHeight * 4); + unsigned char *theTempBuffer = theBuffer.begin(); + CImageScaler::ReduceCols(inOldBuffer, inOldWidth, inOldHeight, theTempBuffer, + inNewWidth); + CImageScaler::ReduceRows(theTempBuffer, inNewWidth, inOldHeight, outNewBuffer, + inNewHeight); + } else if (inOldWidth > inNewWidth) { + CImageScaler::ReduceCols(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, + inNewWidth); + } else if (inOldHeight > inNewHeight) { + CImageScaler::ReduceRows(inOldBuffer, inNewWidth, inOldHeight, outNewBuffer, + inNewHeight); + } + } +} + +void CImageScaler::ExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth, + unsigned long inHeight, unsigned char *outBuffer, + unsigned long inDstWidth, unsigned long inDstHeight, + unsigned long inPlanes) +{ + if (inDstWidth < inWidth || inDstHeight < inHeight) { + return; + } + /*if( inPlanes == 4 ) + { + FastExpandRowsAndColumns( inBuffer, inWidth, inHeight, + outBuffer, inDstWidth, inDstHeight ); + return; + }*/ + unsigned long theYPosition; + unsigned short theYRatio; + unsigned short theYInvRatio; + unsigned long theXPosition; + unsigned short theXRatio; + unsigned short theXInvRatio; + + unsigned long theRow; + unsigned long theColumn; + unsigned long theSrcIndex; + unsigned long theDstIndex; + unsigned long theSrcLineLength; + unsigned long theTemp; + unsigned long thePixel; + + theDstIndex = 0; + theSrcIndex = 0; + theSrcLineLength = inWidth * inPlanes; + theYPosition = inDstHeight; + theYRatio = 1 << 8; + theYInvRatio = 0; + theXInvRatio = 0; + // Here we go.... + // This algorithm will be quite a bit hairy, if you want + // to understand it, then look at the two expand alogorithms above + // and realize the this is just the logical combination of the two + for (theRow = 0; theRow < inDstHeight; theRow++) { + // Run through all the rows, multiplying if necessary the two ratio's together + theXPosition = inDstWidth; + if (theYPosition < inHeight) { + // We have crossed a row boundary + theYRatio = (unsigned short)((theYPosition << 8) / inHeight); + theYInvRatio = (unsigned short)((1 << 8) - theYRatio); + + for (theColumn = 0; theColumn < inDstWidth; theColumn++) { + if (theXPosition < inWidth) { + theXRatio = (unsigned short)((theXPosition << 8) / inWidth); + theXInvRatio = (unsigned short)((1 << 8) - theXRatio); + + // The combination of both the x and y ratio's + unsigned long theLeftRatio = (theXRatio * theYRatio) >> 8; + unsigned long theRightRatio = (theXInvRatio * theYRatio) >> 8; + unsigned long theLowLeftRatio = (theXRatio * theYInvRatio) >> 8; + unsigned long theLowRightRatio = (theXInvRatio * theYInvRatio) >> 8; + // We are on a row and column boundary, thus each pixel here is the + // combination of four pixels (left right, low left, low right) + for (thePixel = 0; thePixel < inPlanes; thePixel++) { + // Left side first + theTemp = (theLeftRatio * inBuffer[theSrcIndex]); + theTemp += (theRightRatio * inBuffer[theSrcIndex + inPlanes]); + theTemp += (theLowLeftRatio * inBuffer[theSrcIndex + theSrcLineLength]); + theTemp += (theLowRightRatio + * inBuffer[theSrcIndex + theSrcLineLength + inPlanes]); + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8); + theDstIndex++; + theSrcIndex++; + } + // Reset our position calculation + theXPosition = inDstWidth - inWidth + theXPosition; + } else { + for (thePixel = 0; thePixel < inPlanes; thePixel++) { + theTemp = theYRatio * inBuffer[theSrcIndex + thePixel]; + theTemp += + theYInvRatio * inBuffer[theSrcIndex + theSrcLineLength + thePixel]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8); + theDstIndex++; + } + // Reset our position calculation + theXPosition -= inWidth; + } + } + // Reset our position calculation + theYPosition = inDstHeight - inHeight + theYPosition; + // Make the src index point to the next line + theSrcIndex += inPlanes; + } // Ends the if to check if we are crossing a row boundary + // Else we are not crossing a row boundary + else { + for (theColumn = 0; theColumn < inDstWidth; theColumn++) { + // If we are crossing a column boundary + if (theXPosition < inWidth) { + theXRatio = (unsigned short)((theXPosition << 8) / inWidth); + theXInvRatio = (unsigned short)((1 << 8) - theXRatio); + for (thePixel = 0; thePixel < inPlanes; thePixel++) { + theTemp = theXRatio * inBuffer[theSrcIndex]; + theTemp += theXInvRatio * inBuffer[theSrcIndex + inPlanes]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8); + theSrcIndex++; + theDstIndex++; + } + + theXPosition = inDstWidth - inWidth + theXPosition; + } + // Else we are not crossing a column boundary + else { + for (thePixel = 0; thePixel < inPlanes; thePixel++) { + outBuffer[theDstIndex] = inBuffer[theSrcIndex + thePixel]; + theDstIndex++; + } + theXPosition -= inWidth; + } + } + // reset our y position indicator + theYPosition -= inHeight; + // reset the src index to the beginning the next line + theSrcIndex += inPlanes; + // reset src index to the beginning of this line + theSrcIndex -= theSrcLineLength; + } // End of else for row boundary + } // End of for loop for iterating through all rows +} + +// Assuming the number of planes is four + +void CImageScaler::FastExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth, + unsigned long inHeight, unsigned char *outBuffer, + unsigned long inDstWidth, unsigned long inDstHeight) +{ + + if (inDstWidth < inWidth || inDstHeight < inHeight) { + return; + } + unsigned long theYPosition; + unsigned short theYRatio; + unsigned short theYInvRatio; + unsigned long theXPosition; + unsigned short theXRatio; + unsigned short theXInvRatio; + // The combination of both the x and y ratio's + unsigned long theLeftRatio; + unsigned long theRightRatio; + unsigned long theLowLeftRatio; + unsigned long theLowRightRatio; + + unsigned long theRow; + unsigned long theColumn; + unsigned long theSrcIndex; + unsigned long theDstIndex; + unsigned long theSrcLineLength; + unsigned long theTemp; + + theDstIndex = 0; + theSrcIndex = 0; + theSrcLineLength = inWidth * 4; + theYPosition = inDstHeight; + theYInvRatio = 0; + theXInvRatio = 0; + // Here we go.... + // This algorithm will be quite a bit hairy, if you want + // to understand it, then look at the two expand alogorithms above + // and realize the this is just the logical combination of the two + for (theRow = 0; theRow < inDstHeight; theRow++) { + // Run through all the rows, multiplying if necessary the two ratio's together + theXPosition = inDstWidth; + if (theYPosition < inHeight) { + // We have crossed a row boundary + theYRatio = (unsigned short)((theYPosition << 8) / inHeight); + theYInvRatio = (unsigned short)((1 << 8) - theYRatio); + + for (theColumn = 0; theColumn < inDstWidth; theColumn++) { + if (theXPosition < inWidth) { + theXRatio = (unsigned short)((theXPosition << 8) / inWidth); + theXInvRatio = (unsigned short)((1 << 8) - theXRatio); + theLeftRatio = (theXRatio * theYRatio) >> 8; + theRightRatio = (theXInvRatio * theYRatio) >> 8; + theLowLeftRatio = (theXRatio * theYInvRatio) >> 8; + theLowRightRatio = (theXInvRatio * theYInvRatio) >> 8; + // We are on a row and column boundary, thus each pixel here is the + // combination of four pixels (left right, low left, low right) + + // Left side first + theTemp = (inBuffer[theSrcIndex]); + theTemp += (inBuffer[theSrcIndex + 4]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]); + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2); + theDstIndex++; + theSrcIndex++; + // Left side first + theTemp = (inBuffer[theSrcIndex]); + theTemp += (inBuffer[theSrcIndex + 4]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]); + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2); + theDstIndex++; + theSrcIndex++; + // Left side first + theTemp = (inBuffer[theSrcIndex]); + theTemp += (inBuffer[theSrcIndex + 4]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]); + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2); + theDstIndex++; + theSrcIndex++; + // Left side first + theTemp = (inBuffer[theSrcIndex]); + theTemp += (inBuffer[theSrcIndex + 4]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength]); + theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]); + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2); + theDstIndex++; + theSrcIndex++; + // Reset our position calculation + theXPosition = inDstWidth - inWidth + theXPosition; + } else { + + theTemp = inBuffer[theSrcIndex]; + theTemp += inBuffer[theSrcIndex + theSrcLineLength]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theDstIndex++; + theTemp = inBuffer[theSrcIndex + 1]; + theTemp += inBuffer[theSrcIndex + theSrcLineLength + 1]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theDstIndex++; + theTemp = inBuffer[theSrcIndex + 2]; + theTemp += inBuffer[theSrcIndex + theSrcLineLength + 2]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theDstIndex++; + theTemp = inBuffer[theSrcIndex + 3]; + theTemp += inBuffer[theSrcIndex + theSrcLineLength + 3]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theDstIndex++; + // Reset our position calculation + theXPosition -= inWidth; + } + } + // Reset our position calculation + theYPosition = inDstHeight - inHeight + theYPosition; + // Make the src index point to the next line + theSrcIndex += 4; + } // Ends the if to check if we are crossing a row boundary + // Else we are not crossing a row boundary + else { + for (theColumn = 0; theColumn < inDstWidth; theColumn++) { + // If we are crossing a column boundary + if (theXPosition < inWidth) { + theXRatio = (unsigned short)((theXPosition << 8) / inWidth); + theXInvRatio = (unsigned short)((1 << 8) - theXRatio); + + theTemp = inBuffer[theSrcIndex]; + theTemp += inBuffer[theSrcIndex + 4]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theSrcIndex++; + theDstIndex++; + theTemp = inBuffer[theSrcIndex]; + theTemp += inBuffer[theSrcIndex + 4]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theSrcIndex++; + theDstIndex++; + theTemp = inBuffer[theSrcIndex]; + theTemp += inBuffer[theSrcIndex + 4]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theSrcIndex++; + theDstIndex++; + theTemp = inBuffer[theSrcIndex]; + theTemp += inBuffer[theSrcIndex + 4]; + outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1); + theSrcIndex++; + theDstIndex++; + + theXPosition = inDstWidth - inWidth + theXPosition; + } + // Else we are not crossing a column boundary + else { + *((long *)(outBuffer + theDstIndex)) = *((long *)(inBuffer + theSrcIndex)); + theDstIndex += 4; + theXPosition -= inWidth; + } + } + // reset our y position indicator + theYPosition -= inHeight; + // reset the src index to the beginning the next line + theSrcIndex += 4; + // reset src index to the beginning of this line + theSrcIndex -= theSrcLineLength; + } // End of else for row boundary + } // End of for loop for iterating through all rows +} + +//============================================================================== +/** + * @param inSrcBuffer + */ +void CImageScaler::ReduceCols(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight, + unsigned char *&outDstBuffer, long inDstWidth) +{ + long theDDAConst = static_cast<long>(1024.0 * inDstWidth / inSrcWidth); + long theDDAAccum = 0L; + long thePixelCount; + + long theSrcRow; + long theSrcCol; + long theDstCol; + + long theRedAccum; + long theGreenAccum; + long theBlueAccum; + long theAlphaAccum; + + unsigned char *theDstPointer = outDstBuffer; + unsigned char *theSrcPointer = inSrcBuffer; + unsigned char *theSrcRowPointer; + unsigned char *theDstRowPointer; + + long theSrcStepSize = 4; + long theDstStepSize = 4; + + for (theSrcRow = 0; theSrcRow < inSrcHeight; ++theSrcRow) { + + theSrcRowPointer = theSrcPointer + (theSrcRow * inSrcWidth * theSrcStepSize); + theDstRowPointer = theDstPointer + (theSrcRow * inDstWidth * theDstStepSize); + + theSrcCol = 0L; + theDstCol = 0L; + theRedAccum = 0L; + theGreenAccum = 0L; + theBlueAccum = 0L; + theAlphaAccum = 0L; + thePixelCount = 0L; + theDDAAccum = 0L; + + while (theSrcCol < inSrcWidth) { + while ((theDDAAccum < 1024L) && (theSrcCol < inSrcWidth)) { + theRedAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 0]; + theGreenAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 1]; + theBlueAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 2]; + theAlphaAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 3]; + + theDDAAccum += theDDAConst; + thePixelCount += 1024L; + ++theSrcCol; + } + + theDDAAccum = (theSrcCol < inSrcWidth) ? (theDDAAccum - 1024L) : (0L); + thePixelCount -= theDDAAccum; + + theRedAccum -= + theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 0]; + theGreenAccum -= + theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 1]; + theBlueAccum -= + theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 2]; + theAlphaAccum -= + theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 3]; + + theDstRowPointer[(theDstCol * theDstStepSize) + 0] = + (unsigned char)(theRedAccum / thePixelCount); + theDstRowPointer[(theDstCol * theDstStepSize) + 1] = + (unsigned char)(theGreenAccum / thePixelCount); + theDstRowPointer[(theDstCol * theDstStepSize) + 2] = + (unsigned char)(theBlueAccum / thePixelCount); + theDstRowPointer[(theDstCol * theDstStepSize) + 3] = + (unsigned char)(theAlphaAccum / thePixelCount); + + thePixelCount = 1024L - theDDAAccum; + ++theDstCol; + + if (theDstCol >= inDstWidth) { + break; + } + + theRedAccum = + thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 0]; + theGreenAccum = + thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 1]; + theBlueAccum = + thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 2]; + theAlphaAccum = + thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 3]; + } + } +} + +//============================================================================== +/** + * @param inSrcBuffer + */ +void CImageScaler::ReduceRows(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight, + unsigned char *&outDstBuffer, long inDstHeight) +{ + long theDDAConst = static_cast<long>(1024.0 * inDstHeight / inSrcHeight); + long theDDAAccum = 0; + long thePixelCount; + + long theSrcRow; + long theSrcCol; + long theDstRow; + + long theRedAccum; + long theGreenAccum; + long theBlueAccum; + long theAlphaAccum; + + unsigned char *theDstPointer = outDstBuffer; + unsigned char *theSrcPointer = inSrcBuffer; + unsigned char *theSrcColPointer = NULL; + unsigned char *theDstColPointer = NULL; + + long theStepSize = 4; + long theSrcStride = 4 * inSrcWidth; + long theDstStride = 4 * inSrcWidth; + + for (theSrcCol = 0; theSrcCol < inSrcWidth; ++theSrcCol) { + theSrcColPointer = theSrcPointer + (theSrcCol * theStepSize); + theDstColPointer = theDstPointer + (theSrcCol * theStepSize); + + theSrcRow = 0L; + theDstRow = 0L; + theRedAccum = 0L; + theGreenAccum = 0L; + theBlueAccum = 0L; + theAlphaAccum = 0L; + thePixelCount = 0L; + + theDDAAccum = 0L; + + while (theSrcRow < inSrcHeight) { + while ((theDDAAccum < 1024L) && (theSrcRow < inSrcHeight)) { + theRedAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 0]; + theGreenAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 1]; + theBlueAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 2]; + theAlphaAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 3]; + + theDDAAccum += theDDAConst; + thePixelCount += 1024L; + ++theSrcRow; + } + + theDDAAccum = (theSrcRow < inSrcHeight) ? (theDDAAccum - 1024L) : (0L); + thePixelCount -= theDDAAccum; + + theRedAccum -= + theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 0]; + theGreenAccum -= + theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 1]; + theBlueAccum -= + theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 2]; + theAlphaAccum -= + theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 3]; + + theDstColPointer[(theDstRow * theDstStride) + 0] = + (unsigned char)(theRedAccum / thePixelCount); + theDstColPointer[(theDstRow * theDstStride) + 1] = + (unsigned char)(theGreenAccum / thePixelCount); + theDstColPointer[(theDstRow * theDstStride) + 2] = + (unsigned char)(theBlueAccum / thePixelCount); + theDstColPointer[(theDstRow * theDstStride) + 3] = + (unsigned char)(theAlphaAccum / thePixelCount); + + thePixelCount = 1024L - theDDAAccum; + ++theDstRow; + + if (theDstRow >= inDstHeight) { + break; + } + + theRedAccum = + thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 0]; + theGreenAccum = + thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 1]; + theBlueAccum = + thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 2]; + theAlphaAccum = + thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 3]; + } + } +} diff --git a/src/runtimerender/Qt3DSRenderImageScaler.h b/src/runtimerender/Qt3DSRenderImageScaler.h new file mode 100644 index 0000000..27b5177 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderImageScaler.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 1999-2001 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ + +//============================================================================== +// Prefix +//============================================================================== +#ifndef __IMAGESCALER_H_ +#define __IMAGESCALER_H_ +#include "Qt3DSRender.h" + +namespace qt3ds { +namespace render { + //============================================================================== + // Class + //============================================================================== + //============================================================================== + /** + * @class CImageScaler + */ + //============================================================================== + class CImageScaler + { + NVAllocatorCallback &m_Allocator; + + public: + //============================================================================== + // Methods + //============================================================================== + enum EScaleMethod { + SCALEMETHOD_CROP = -1, // Debug only, not a scaler + SCALEMETHOD_POINTSAMPLE = 0, + SCALEMETHOD_BILINEAR = 1, + }; + + //============================================================================== + // Methods + //============================================================================== + + // Access + + public: + CImageScaler(NVAllocatorCallback &inAlloc); + + void Scale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, unsigned long inChannels); + + void FastScale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer, + unsigned long inOldWidth, unsigned long inOldHeight, + unsigned char *&outNewBuffer, unsigned long inNewWidth, + unsigned long inNewHeight, unsigned long inChannels); + + void Crop(unsigned char *inOldBuffer, unsigned long inOldWidth, unsigned long inOldHeight, + unsigned char *&outNewBuffer, unsigned long inNewWidth, unsigned long inNewHeight, + unsigned long inPlanes); + + void Bilinear(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, unsigned long inPlanes); + + void FastPointSample(unsigned char *inOldBuffer, unsigned long inOldWidth, + unsigned long inOldHeight, unsigned char *&outNewBuffer, + unsigned long inNewWidth, unsigned long inNewHeight, + unsigned long inPlanes); + + unsigned char *AllocateBuffer(long inWidth, long inHeight); + void ReleaseBuffer(unsigned char *&ioBuffer); + void Resize(unsigned char *inOldBuffer, unsigned long inOldWidth, unsigned long inOldHeight, + unsigned char *&outNewBuffer, unsigned long inNewWidth, + unsigned long inNewHeight, unsigned long inPlanes); + + // variable numbers of planes, i.e. greyscale, rb, rbg, and rgba or argb + // Bilinear algorithms, good for quality + void ExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth, + unsigned long inHeight, unsigned char *outBuffer, + unsigned long inDstWidth, unsigned long inDstHeight, + unsigned long inPlanes); + + // The method implemented above, but with some optimizations + // specifically, fixed the number of planes at 4 + // eliminated the new/delete allocations + void FastExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth, + unsigned long inHeight, unsigned char *outBuffer, + unsigned long inDstWidth, unsigned long inDstHeight); + + void ReduceCols(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight, + unsigned char *&outDstBuffer, long inDstWidth); + void ReduceRows(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight, + unsigned char *&outDstBuffer, long inDstHeight); + }; +} +} + +#endif // !defined(__IMAGESCALER_H_) diff --git a/src/runtimerender/Qt3DSRenderImageTextureData.h b/src/runtimerender/Qt3DSRenderImageTextureData.h new file mode 100644 index 0000000..aaa73d7 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderImageTextureData.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_IMAGE_TEXTURE_DATA_H +#define QT3DS_RENDER_IMAGE_TEXTURE_DATA_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSFlags.h" + +#include <QtCore/qsharedpointer.h> +#include <QtCore/qvector.h> + +namespace qt3ds { +namespace render { + + // forward declararion + class Qt3DSRenderPrefilterTexture; + + struct ImageTextureFlagValues + { + enum Enum { + HasTransparency = 1, + InvertUVCoords = 1 << 1, + PreMultiplied = 1 << 2, + }; + }; + + struct SImageTextureFlags : public NVFlags<ImageTextureFlagValues::Enum, QT3DSU32> + { + bool HasTransparency() const + { + return this->operator&(ImageTextureFlagValues::HasTransparency); + } + void SetHasTransparency(bool inValue) + { + clearOrSet(inValue, ImageTextureFlagValues::HasTransparency); + } + + bool IsInvertUVCoords() const + { + return this->operator&(ImageTextureFlagValues::InvertUVCoords); + } + void SetInvertUVCoords(bool inValue) + { + clearOrSet(inValue, ImageTextureFlagValues::InvertUVCoords); + } + + bool IsPreMultiplied() const + { + return this->operator&(ImageTextureFlagValues::PreMultiplied); + } + void SetPreMultiplied(bool inValue) + { + clearOrSet(inValue, ImageTextureFlagValues::PreMultiplied); + } + }; + + struct SImageTextureData + { + NVRenderTexture2D *m_Texture; + SImageTextureFlags m_TextureFlags; + Qt3DSRenderPrefilterTexture *m_BSDFMipMap; + + SImageTextureData() + : m_Texture(nullptr) + , m_BSDFMipMap(nullptr) + { + } + + SImageTextureData(const SImageTextureData& data) + : m_Texture(data.m_Texture), m_TextureFlags(data.m_TextureFlags) + , m_BSDFMipMap(data.m_BSDFMipMap) + { + + } + + bool operator!=(const SImageTextureData &inOther) + { + return m_Texture != inOther.m_Texture || m_TextureFlags != inOther.m_TextureFlags + || m_BSDFMipMap != inOther.m_BSDFMipMap; + } + }; + + struct IReloadableCallback + { + virtual ~IReloadableCallback() {} + virtual void onLoad() = 0; + virtual void onUnload() = 0; + }; + + struct SImage; + struct SReloadableImageTextureData : public SImageTextureData + { + QString m_path; + bool m_loaded; + bool m_scanTransparency; + bool m_bsdfMipmap; + bool m_initialized; + QVector<SImage *> m_callbacks; + + SReloadableImageTextureData() + : SImageTextureData() + , m_loaded(false), m_scanTransparency(false), m_bsdfMipmap(false), m_initialized(false) + { + } + }; + + typedef QSharedPointer<SReloadableImageTextureData> ReloadableTexturePtr; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp b/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp new file mode 100644 index 0000000..1a418b1 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderInputStreamFactory.h" + +#include "stdio.h" + +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "EASTL/string.h" +#include "EASTL/vector.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/FileTools.h" +#include "foundation/Qt3DSMutex.h" + +#include <QDir> +#include <QDirIterator> +#include <QFile> +#include <QFileInfo> +#include <QUrl> + +using namespace qt3ds::render; + +namespace { +struct SInputStream : public IRefCountedInputStream +{ + NVFoundationBase &m_Foundation; + QString m_Path; + QFile m_File; + volatile QT3DSI32 mRefCount; + + SInputStream(NVFoundationBase &inFoundation, const QString &inPath) + : m_Foundation(inFoundation) + , m_Path(inPath) + , m_File(inPath) + , mRefCount(0) + { + m_File.open(QIODevice::ReadOnly); + } + virtual ~SInputStream() + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override + { + return m_File.read((char *)data.begin(), data.size()); + } + + bool Write(NVConstDataRef<QT3DSU8> /*data*/) override + { + QT3DS_ASSERT(false); + return false; + } + + void SetPosition(QT3DSI64 inOffset, qt3ds::foundation::SeekPosition::Enum inEnum) override + { + if (inOffset > QT3DS_MAX_I32 || inOffset < QT3DS_MIN_I32) { + qCCritical(INVALID_OPERATION, "Attempt to seek further than platform allows"); + QT3DS_ASSERT(false); + return; + } else { + CFileTools::SetStreamPosition(m_File, inOffset, inEnum); + } + } + QT3DSI64 GetPosition() const override + { + return m_File.pos(); + } + + static SInputStream *OpenFile(const QString &inPath, NVFoundationBase &inFoundation) + { + return QT3DS_NEW(inFoundation.getAllocator(), SInputStream)(inFoundation, inPath); + } +}; + +typedef eastl::basic_string<char8_t, ForwardingAllocator> TStrType; +struct SFactory : public IInputStreamFactory +{ + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + + Mutex m_Mutex; + typedef Mutex::ScopedLock TScopedLock; + + const QString QT3DSTUDIO_TAG = QStringLiteral("qt3dstudio"); + + SFactory(NVFoundationBase &inFoundation) + : m_Foundation(inFoundation) + , mRefCount(0) + , m_Mutex(inFoundation.getAllocator()) + { + // Add the top-level qrc directory + if (!QDir::searchPaths(QT3DSTUDIO_TAG).contains(QLatin1String(":/"))) + QDir::addSearchPath(QT3DSTUDIO_TAG, QStringLiteral(":/")); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + QFileInfo matchCaseInsensitiveFile(const QString& file, bool inQuiet) + { + if (!inQuiet) { + // Some assets are searched for in several levels in the project structure, + // we don't want to alert user of things that can't be fixed in the presentation + // itself. + qCWarning(WARNING, PERF_INFO, "Case-insensitive matching with file: %s", + file.toLatin1().constData()); + } + const QStringList searchDirectories = QDir::searchPaths(QT3DSTUDIO_TAG); + for (const auto &directoryPath : searchDirectories) { + QFileInfo fileInfo(file); + QDirIterator it(directoryPath, {fileInfo.fileName()}, QDir::NoFilter, + QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = it.next(); + if (filePath.compare(QDir::cleanPath(directoryPath + '/' + file), + Qt::CaseInsensitive) == 0) { + return QFileInfo(filePath); + } + } + } + + return QFileInfo(); + } + + void AddSearchDirectory(const char8_t *inDirectory) override + { + TScopedLock __factoryLocker(m_Mutex); + QString localDir = CFileTools::NormalizePathForQtUsage(inDirectory); + QDir directory(localDir); + if (!directory.exists()) { + qCCritical(INTERNAL_ERROR, "Adding search directory: %s", inDirectory); + return; + } + + if (!QDir::searchPaths(QT3DSTUDIO_TAG).contains(localDir)) + QDir::addSearchPath(QT3DSTUDIO_TAG, localDir); + } + + + IRefCountedInputStream *GetStreamForFile(const QString &inFilename, bool inQuiet) override + { + TScopedLock __factoryLocker(m_Mutex); + QString localFile = CFileTools::NormalizePathForQtUsage(inFilename); + QFileInfo fileInfo = QFileInfo(localFile); + SInputStream *inputStream = nullptr; + // Try to match the file with the search paths + if (!fileInfo.exists()) + fileInfo.setFile(QStringLiteral("qt3dstudio:") + localFile); + + // Try to match the case-insensitive file with the given search paths + if (!fileInfo.exists()) + fileInfo = matchCaseInsensitiveFile(localFile, inQuiet); + + if (fileInfo.exists()) + inputStream = SInputStream::OpenFile(fileInfo.absoluteFilePath(), m_Foundation); + + if (!inputStream && !inQuiet) { + // Print extensive debugging information. + qCCritical(INTERNAL_ERROR, "Failed to find file: %s", localFile.toLatin1().constData()); + qCCritical(INTERNAL_ERROR, "Searched path: %s", + QDir::searchPaths(QT3DSTUDIO_TAG).join(',').toLatin1().constData()); + } + return inputStream; + } + + bool GetPathForFile(const QString &inFilename, QString &outFile, + bool inQuiet = false) override + { + NVScopedRefCounted<IRefCountedInputStream> theStream = + GetStreamForFile(inFilename, inQuiet); + if (theStream) { + SInputStream *theRealStream = static_cast<SInputStream *>(theStream.mPtr); + outFile = theRealStream->m_Path; + return true; + } + return false; + } +}; +} + +IInputStreamFactory &IInputStreamFactory::Create(NVFoundationBase &inFoundation) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), SFactory)(inFoundation); +} diff --git a/src/runtimerender/Qt3DSRenderInputStreamFactory.h b/src/runtimerender/Qt3DSRenderInputStreamFactory.h new file mode 100644 index 0000000..e82134b --- /dev/null +++ b/src/runtimerender/Qt3DSRenderInputStreamFactory.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_INPUT_STREAM_FACTORY_H +#define QT3DS_RENDER_INPUT_STREAM_FACTORY_H +#include "Qt3DSRender.h" +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSRefCounted.h" +#include "EASTL/string.h" + +namespace qt3ds { +namespace render { + class IRefCountedInputStream : public qt3ds::foundation::ISeekableIOStream, public NVRefCounted + { + protected: + virtual ~IRefCountedInputStream() {} + }; + // This class is threadsafe. + class IInputStreamFactory : public NVRefCounted + { + protected: + virtual ~IInputStreamFactory() {} + public: + // These directories must have a '/' on them + virtual void AddSearchDirectory(const char8_t *inDirectory) = 0; + virtual IRefCountedInputStream *GetStreamForFile(const QString &inFilename, + bool inQuiet = false) = 0; + // Return a path for this file. Returns true if GetStreamForFile would return a valid + // stream. + // else returns false + virtual bool GetPathForFile(const QString &inFilename, QString &outFile, + bool inQuiet = false) = 0; + + // Create an input stream factory using this foundation and an platform-optional app + // directory + // on android the app directory has no effect; use use the assets bundled with the APK file. + static IInputStreamFactory &Create(NVFoundationBase &inFoundation); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderLightConstantProperties.h b/src/runtimerender/Qt3DSRenderLightConstantProperties.h new file mode 100644 index 0000000..4dcd62c --- /dev/null +++ b/src/runtimerender/Qt3DSRenderLightConstantProperties.h @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 QT3DS_RENDER_LIGHT_CONSTANT_PROPERTIES +#define QT3DS_RENDER_LIGHT_CONSTANT_PROPERTIES + +#include "render/Qt3DSRenderShaderProgram.h" + +namespace qt3ds { +namespace render { + +static const QStringList lconstantnames = { + QStringLiteral("position"), + QStringLiteral("direction"), + QStringLiteral("up"), + QStringLiteral("right"), + QStringLiteral("diffuse"), + QStringLiteral("ambient"), + QStringLiteral("specular"), + QStringLiteral("spotExponent"), + QStringLiteral("spotCutoff"), + QStringLiteral("constantAttenuation"), + QStringLiteral("linearAttenuation"), + QStringLiteral("quadraticAttenuation"), + QStringLiteral("range"), + QStringLiteral("width"), + QStringLiteral("height"), + QStringLiteral("shadowControls"), + QStringLiteral("shadowView"), + QStringLiteral("shadowIdx"), + QStringLiteral("attenuation") +}; + +#define LCSEED QStringLiteral("%1%2") + +template <typename GeneratedShader> +struct SLightConstantProperties +{ + struct LightConstants + { + NVRenderCachedShaderProperty<QT3DSVec4> m_position; + NVRenderCachedShaderProperty<QT3DSVec4> m_direction; + NVRenderCachedShaderProperty<QT3DSVec4> m_up; + NVRenderCachedShaderProperty<QT3DSVec4> m_right; + NVRenderCachedShaderProperty<QT3DSVec4> m_diffuse; + NVRenderCachedShaderProperty<QT3DSVec4> m_ambient; + NVRenderCachedShaderProperty<QT3DSVec4> m_specular; + NVRenderCachedShaderProperty<QT3DSF32> m_spotExponent; + NVRenderCachedShaderProperty<QT3DSF32> m_spotCutoff; + NVRenderCachedShaderProperty<QT3DSF32> m_constantAttenuation; + NVRenderCachedShaderProperty<QT3DSF32> m_linearAttenuation; + NVRenderCachedShaderProperty<QT3DSF32> m_quadraticAttenuation; + NVRenderCachedShaderProperty<QT3DSF32> m_range; + NVRenderCachedShaderProperty<QT3DSF32> m_width; + NVRenderCachedShaderProperty<QT3DSF32> m_height; + NVRenderCachedShaderProperty<QT3DSVec4> m_shadowControls; + NVRenderCachedShaderProperty<QT3DSMat44> m_shadowView; + NVRenderCachedShaderProperty<QT3DSI32> m_shadowIdx; + NVRenderCachedShaderProperty<QT3DSVec3> m_attenuation; + + LightConstants(const QString &lightRef, render::NVRenderShaderProgram &shader) + : m_position(LCSEED.arg(lightRef, lconstantnames[0]), shader) + , m_direction(LCSEED.arg(lightRef).arg(lconstantnames[1]), shader) + , m_up(LCSEED.arg(lightRef, lconstantnames[2]), shader) + , m_right(LCSEED.arg(lightRef, lconstantnames[3]), shader) + , m_diffuse(LCSEED.arg(lightRef, lconstantnames[4]), shader) + , m_ambient(LCSEED.arg(lightRef, lconstantnames[5]), shader) + , m_specular(LCSEED.arg(lightRef, lconstantnames[6]), shader) + , m_spotExponent(LCSEED.arg(lightRef, lconstantnames[7]), shader) + , m_spotCutoff(LCSEED.arg(lightRef, lconstantnames[8]), shader) + , m_constantAttenuation(LCSEED.arg(lightRef, lconstantnames[9]), shader) + , m_linearAttenuation(LCSEED.arg(lightRef, lconstantnames[10]), shader) + , m_quadraticAttenuation(LCSEED.arg(lightRef, lconstantnames[11]), shader) + , m_range(LCSEED.arg(lightRef, lconstantnames[12]), shader) + , m_width(LCSEED.arg(lightRef, lconstantnames[13]), shader) + , m_height(LCSEED.arg(lightRef, lconstantnames[14]), shader) + , m_shadowControls(LCSEED.arg(lightRef, lconstantnames[15]), shader) + , m_shadowView(LCSEED.arg(lightRef, lconstantnames[16]), shader) + , m_shadowIdx(LCSEED.arg(lightRef, lconstantnames[17]), shader) + , m_attenuation(LCSEED.arg(lightRef, lconstantnames[18]), shader) + { + + } + + template <typename LightProps> + void updateLights(LightProps &props) + { + m_position.Set(props.m_position); + m_direction.Set(props.m_direction); + m_up.Set(props.m_up); + m_right.Set(props.m_right); + m_diffuse.Set(props.m_diffuse); + m_ambient.Set(props.m_ambient); + m_specular.Set(props.m_specular); + m_spotExponent.Set(props.m_spotExponent); + m_spotCutoff.Set(props.m_spotCutoff); + m_constantAttenuation.Set(props.m_constantAttenuation); + m_linearAttenuation.Set(props.m_linearAttenuation); + m_quadraticAttenuation.Set(props.m_quadraticAttenuation); + m_range.Set(props.m_range); + m_width.Set(props.m_width); + m_height.Set(props.m_height); + m_shadowControls.Set(props.m_shadowControls); + m_shadowView.Set(props.m_shadowView); + m_shadowIdx.Set(props.m_shadowIdx); + m_attenuation.Set(QT3DSVec3(props.m_constantAttenuation, + props.m_linearAttenuation, + props.m_quadraticAttenuation)); + } + }; + + SLightConstantProperties(GeneratedShader &shader, bool packed) + : m_lightCount("uNumLights", shader.m_Shader) + { + m_constants.resize(shader.m_Lights.size()); + for (unsigned int i = 0; i < shader.m_Lights.size(); ++i) { + QString lref; + if (packed) + lref = QStringLiteral("light_%1_"); + else + lref = QStringLiteral("lights[%1]."); + lref = lref.arg(i); + m_constants[i] = new LightConstants(lref, shader.m_Shader); + } + m_lightCount.Set(shader.m_Lights.size()); + m_lightCountInt = shader.m_Lights.size(); + } + + SLightConstantProperties(const QString &lseed, const QString &lcount, + GeneratedShader &shader, bool packed, int count) + : m_lightCount(lcount, shader.m_Shader) + { + m_constants.resize(count); + for (int i = 0; i < count; ++i) { + QString lref; + if (packed) + lref = lseed + QStringLiteral("_%1_"); + else + lref = lseed + QStringLiteral("[%1]."); + lref = lref.arg(i); + m_constants[i] = new LightConstants(lref, shader.m_Shader); + } + m_lightCount.Set(count); + m_lightCountInt = count; + } + + ~SLightConstantProperties() + { + qDeleteAll(m_constants); + } + + void updateLights(GeneratedShader &shader) + { + for (int i = 0; i < m_constants.size(); ++i) + m_constants[i]->updateLights(shader.m_Lights[i].m_LightData); + } + template <typename LightProps> + void updateLights(const QVector<LightProps*> &props) + { + for (int i = 0; i < m_constants.size(); ++i) + m_constants[i]->updateLights(props[i]->m_LightData); + } + + QVector<LightConstants *> m_constants; + NVRenderCachedShaderProperty<QT3DSI32> m_lightCount; + int m_lightCountInt; +}; + +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderMaterialHelpers.h b/src/runtimerender/Qt3DSRenderMaterialHelpers.h new file mode 100644 index 0000000..afb799e --- /dev/null +++ b/src/runtimerender/Qt3DSRenderMaterialHelpers.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_MATERIAL_HELPERS_H +#define QT3DS_RENDER_MATERIAL_HELPERS_H +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderReferencedMaterial.h" + +namespace qt3ds { +namespace render { + + inline bool IsMaterial(SGraphObject &obj) + { + return obj.m_Type == GraphObjectTypes::CustomMaterial + || obj.m_Type == GraphObjectTypes::DefaultMaterial + || obj.m_Type == GraphObjectTypes::ReferencedMaterial; + } + + inline bool IsMaterial(SGraphObject *obj) + { + if (obj) + return IsMaterial(*obj); + return false; + } + + inline bool IsImage(SGraphObject &obj) { return obj.m_Type == GraphObjectTypes::Image; } + + inline bool IsImage(SGraphObject *obj) + { + if (obj) + return IsImage(*obj); + return false; + } + + inline SGraphObject *GetNextMaterialSibling(SGraphObject *obj) + { + if (obj == NULL) + return NULL; + if (IsMaterial(obj) == false) { + QT3DS_ASSERT(false); + return NULL; + } + if (obj->m_Type == GraphObjectTypes::CustomMaterial) + return static_cast<SCustomMaterial *>(obj)->m_NextSibling; + else if (obj->m_Type == GraphObjectTypes::DefaultMaterial) + return static_cast<SDefaultMaterial *>(obj)->m_NextSibling; + else + return static_cast<SReferencedMaterial *>(obj)->m_NextSibling; + } + + inline void SetNextMaterialSibling(SGraphObject &obj, SGraphObject *sibling) + { + if (IsMaterial(obj) == false) { + QT3DS_ASSERT(false); + return; + } + if (obj.m_Type == GraphObjectTypes::CustomMaterial) + static_cast<SCustomMaterial *>(&obj)->m_NextSibling = sibling; + else if (obj.m_Type == GraphObjectTypes::DefaultMaterial) + static_cast<SDefaultMaterial *>(&obj)->m_NextSibling = sibling; + else + static_cast<SReferencedMaterial *>(&obj)->m_NextSibling = sibling; + } +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderMaterialShaderGenerator.h b/src/runtimerender/Qt3DSRenderMaterialShaderGenerator.h new file mode 100644 index 0000000..e8b9880 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderMaterialShaderGenerator.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_MATERIAL_SHADER_GENERATOR_H +#define QT3DS_RENDER_MATERIAL_SHADER_GENERATOR_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" + +namespace qt3ds { +namespace render { + +// these are our current shader limits +#define QT3DS_MAX_NUM_LIGHTS 16 +#define QT3DS_MAX_NUM_SHADOWS 8 + + // note this struct must exactly match the memory layout of the + // struct sampleLight.glsllib and sampleArea.glsllib. If you make changes here you need + // to adjust the code in sampleLight.glsllib and sampleArea.glsllib as well + struct SLightSourceShader + { + QT3DSVec4 m_position; + QT3DSVec4 m_direction; // Specifies the light direction in world coordinates. + QT3DSVec4 m_up; + QT3DSVec4 m_right; + QT3DSVec4 m_diffuse; + QT3DSVec4 m_ambient; + QT3DSVec4 m_specular; + QT3DSF32 m_spotExponent; // Specifies the intensity distribution of the light. + QT3DSF32 m_spotCutoff; // Specifies the maximum spread angle of the light. + QT3DSF32 m_constantAttenuation; // Specifies the constant light attenuation factor. + QT3DSF32 m_linearAttenuation; // Specifies the linear light attenuation factor. + QT3DSF32 m_quadraticAttenuation; // Specifies the quadratic light attenuation factor. + QT3DSF32 m_range; // Specifies the maximum distance of the light influence + QT3DSF32 m_width; // Specifies the width of the area light surface. + QT3DSF32 m_height; // Specifies the height of the area light surface; + QT3DSVec4 m_shadowControls; + QT3DSMat44 m_shadowView; + QT3DSI32 m_shadowIdx; + QT3DSF32 m_padding1[3]; + }; + + struct SLayerGlobalRenderProperties + { + const SLayer &m_Layer; + SCamera &m_Camera; + QT3DSVec3 m_CameraDirection; + NVDataRef<SLight *> m_Lights; + NVDataRef<QT3DSVec3> m_LightDirections; + Qt3DSShadowMap *m_ShadowMapManager; + NVRenderTexture2D *m_DepthTexture; + NVRenderTexture2D *m_SSaoTexture; + SImage *m_LightProbe; + SImage *m_LightProbe2; + QT3DSF32 m_ProbeHorizon; + QT3DSF32 m_ProbeBright; + QT3DSF32 m_Probe2Window; + QT3DSF32 m_Probe2Pos; + QT3DSF32 m_Probe2Fade; + QT3DSF32 m_ProbeFOV; + + SLayerGlobalRenderProperties(const SLayer &inLayer, SCamera &inCamera, + QT3DSVec3 inCameraDirection, NVDataRef<SLight *> inLights, + NVDataRef<QT3DSVec3> inLightDirections, + Qt3DSShadowMap *inShadowMapManager, + NVRenderTexture2D *inDepthTexture, + NVRenderTexture2D *inSSaoTexture, SImage *inLightProbe, + SImage *inLightProbe2, QT3DSF32 inProbeHorizon, + QT3DSF32 inProbeBright, QT3DSF32 inProbe2Window, QT3DSF32 inProbe2Pos, + QT3DSF32 inProbe2Fade, QT3DSF32 inProbeFOV) + : m_Layer(inLayer) + , m_Camera(inCamera) + , m_CameraDirection(inCameraDirection) + , m_Lights(inLights) + , m_LightDirections(inLightDirections) + , m_ShadowMapManager(inShadowMapManager) + , m_DepthTexture(inDepthTexture) + , m_SSaoTexture(inSSaoTexture) + , m_LightProbe(inLightProbe) + , m_LightProbe2(inLightProbe2) + , m_ProbeHorizon(inProbeHorizon) + , m_ProbeBright(inProbeBright) + , m_Probe2Window(inProbe2Window) + , m_Probe2Pos(inProbe2Pos) + , m_Probe2Fade(inProbe2Fade) + , m_ProbeFOV(inProbeFOV) + { + } + }; + + class IMaterialShaderGenerator : public NVRefCounted + { + public: + struct SImageVariableNames + { + const char8_t *m_ImageSampler; + const char8_t *m_ImageFragCoords; + }; + + virtual SImageVariableNames GetImageVariableNames(QT3DSU32 inIdx) = 0; + virtual void GenerateImageUVCoordinates(IShaderStageGenerator &inVertexPipeline, QT3DSU32 idx, + QT3DSU32 uvSet, SRenderableImage &image) = 0; + + // inPipelineName needs to be unique else the shader cache will just return shaders from + // different pipelines. + virtual NVRenderShaderProgram *GenerateShader( + const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, bool inHasTransparency, + const char8_t *inVertexPipelineName, const char8_t *inCustomMaterialName = "") = 0; + + // Also sets the blend function on the render context. + virtual void + SetMaterialProperties(NVRenderShaderProgram &inProgram, const SGraphObject &inMaterial, + const QT3DSVec2 &inCameraVec, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) = 0; + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderMesh.h b/src/runtimerender/Qt3DSRenderMesh.h new file mode 100644 index 0000000..d6959ec --- /dev/null +++ b/src/runtimerender/Qt3DSRenderMesh.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_MESH_H +#define QT3DS_RENDER_MESH_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "render/Qt3DSRenderIndexBuffer.h" +#include "render/Qt3DSRenderInputAssembler.h" +#include "foundation/Qt3DSBounds3.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSNoCopy.h" + +namespace qt3ds { +namespace render { + + struct SRenderSubsetBase + { + QT3DSU32 m_Count; + QT3DSU32 m_Offset; + NVBounds3 m_Bounds; // Vertex buffer bounds + SRenderSubsetBase() {} + SRenderSubsetBase(const SRenderSubsetBase &inOther) + : m_Count(inOther.m_Count) + , m_Offset(inOther.m_Offset) + , m_Bounds(inOther.m_Bounds) + { + } + + SRenderSubsetBase &operator=(const SRenderSubsetBase &inOther) + { + m_Count = inOther.m_Count; + m_Offset = inOther.m_Offset; + m_Bounds = inOther.m_Bounds; + return *this; + } + }; + + struct SRenderJoint + { + QT3DSI32 m_JointID; + QT3DSI32 m_ParentID; + QT3DSF32 m_invBindPose[16]; + QT3DSF32 m_localToGlobalBoneSpace[16]; + }; + + struct SRenderSubset : public SRenderSubsetBase + { + NVRenderInputAssembler *m_InputAssembler; + NVRenderInputAssembler *m_InputAssemblerDepth; + NVRenderInputAssembler + *m_InputAssemblerPoints; ///< similar to depth but ignores index buffer. + NVRenderVertexBuffer *m_VertexBuffer; + NVRenderVertexBuffer + *m_PosVertexBuffer; ///< separate position buffer for fast depth path rendering + NVRenderIndexBuffer *m_IndexBuffer; + NVRenderDrawMode::Enum m_PrimitiveType; ///< primitive type used for drawing + QT3DSF32 m_EdgeTessFactor; ///< edge tessellation amount used for tessellation shaders + QT3DSF32 m_InnerTessFactor; ///< inner tessellation amount used for tessellation shaders + bool m_WireframeMode; ///< true if we should draw the object as wireframe ( currently ony if + ///tessellation is enabled ) + NVConstDataRef<SRenderJoint> m_Joints; + CRegisteredString m_Name; + nvvector<SRenderSubsetBase> m_SubSubsets; + + SRenderSubset(NVAllocatorCallback &alloc) + : m_InputAssembler(NULL) + , m_InputAssemblerDepth(NULL) + , m_InputAssemblerPoints(NULL) + , m_VertexBuffer(NULL) + , m_PosVertexBuffer(NULL) + , m_IndexBuffer(NULL) + , m_PrimitiveType(NVRenderDrawMode::Triangles) + , m_EdgeTessFactor(1.0) + , m_InnerTessFactor(1.0) + , m_WireframeMode(false) + , m_SubSubsets(alloc, "SRenderSubset::m_SubSubsets") + { + } + SRenderSubset(const SRenderSubset &inOther) + : SRenderSubsetBase(inOther) + , m_InputAssembler(inOther.m_InputAssembler) + , m_InputAssemblerDepth(inOther.m_InputAssemblerDepth) + , m_InputAssemblerPoints(inOther.m_InputAssemblerPoints) + , m_VertexBuffer(inOther.m_VertexBuffer) + , m_PosVertexBuffer(inOther.m_PosVertexBuffer) + , m_IndexBuffer(inOther.m_IndexBuffer) + , m_PrimitiveType(inOther.m_PrimitiveType) + , m_EdgeTessFactor(inOther.m_EdgeTessFactor) + , m_InnerTessFactor(inOther.m_InnerTessFactor) + , m_WireframeMode(inOther.m_WireframeMode) + , m_Joints(inOther.m_Joints) + , m_Name(inOther.m_Name) + , m_SubSubsets(inOther.m_SubSubsets) + { + } + // Note that subSubsets is *not* copied. + SRenderSubset(NVAllocatorCallback &alloc, const SRenderSubset &inOther, + const SRenderSubsetBase &inBase) + : SRenderSubsetBase(inBase) + , m_InputAssembler(inOther.m_InputAssembler) + , m_InputAssemblerDepth(inOther.m_InputAssemblerDepth) + , m_InputAssemblerPoints(inOther.m_InputAssemblerPoints) + , m_VertexBuffer(inOther.m_VertexBuffer) + , m_PosVertexBuffer(inOther.m_PosVertexBuffer) + , m_IndexBuffer(inOther.m_IndexBuffer) + , m_PrimitiveType(inOther.m_PrimitiveType) + , m_EdgeTessFactor(inOther.m_EdgeTessFactor) + , m_InnerTessFactor(inOther.m_InnerTessFactor) + , m_WireframeMode(inOther.m_WireframeMode) + , m_Name(inOther.m_Name) + , m_SubSubsets(alloc, "SRenderSubset::m_SubSubsets") + { + } + + SRenderSubset &operator=(const SRenderSubset &inOther) + { + if (this != &inOther) { + SRenderSubsetBase::operator=(inOther); + m_InputAssembler = inOther.m_InputAssembler; + m_InputAssemblerDepth = inOther.m_InputAssemblerDepth; + m_VertexBuffer = inOther.m_VertexBuffer; + m_PosVertexBuffer = inOther.m_PosVertexBuffer; + m_IndexBuffer = inOther.m_IndexBuffer; + m_PrimitiveType = inOther.m_PrimitiveType; + m_EdgeTessFactor = inOther.m_EdgeTessFactor; + m_InnerTessFactor = inOther.m_InnerTessFactor; + m_WireframeMode = inOther.m_WireframeMode; + m_Joints = inOther.m_Joints; + m_Name = inOther.m_Name; + m_SubSubsets = inOther.m_SubSubsets; + } + return *this; + } + }; + + struct SRenderMesh : public NoCopy + { + nvvector<SRenderSubset> m_Subsets; + nvvector<SRenderJoint> m_Joints; + NVRenderDrawMode::Enum m_DrawMode; + NVRenderWinding::Enum m_Winding; // counterclockwise + QT3DSU32 m_MeshId; // Id from the file of this mesh. + + SRenderMesh(NVRenderDrawMode::Enum inDrawMode, NVRenderWinding::Enum inWinding, + QT3DSU32 inMeshId, NVAllocatorCallback &alloc) + : m_Subsets(alloc, "SRenderMesh::m_Subsets") + , m_Joints(alloc, "SRenderMesh::Joints") + , m_DrawMode(inDrawMode) + , m_Winding(inWinding) + , m_MeshId(inMeshId) + { + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderPathManager.cpp b/src/runtimerender/Qt3DSRenderPathManager.cpp new file mode 100644 index 0000000..5b2c51d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPathManager.cpp @@ -0,0 +1,1964 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPathManager.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSContainers.h" +#include "EASTL/string.h" +#include "Qt3DSRenderContextCore.h" +#include "foundation/Utils.h" +#include "foundation/StringConversionImpl.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "render/Qt3DSRenderInputAssembler.h" +#include "Qt3DSRenderPath.h" +#include "EASTL/sort.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderPathRenderContext.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "Qt3DSRenderCustomMaterialShaderGenerator.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "rendererimpl/Qt3DSVertexPipelineImpl.h" +#include "foundation/Qt3DSMathUtils.h" +#include "render/Qt3DSRenderPathRender.h" +#include "render/Qt3DSRenderPathSpecification.h" +#include "Qt3DSRenderPathSubPath.h" +#include "Qt3DSImportPath.h" +#include "Qt3DSRenderPathMath.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "foundation/Qt3DSMutex.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; +using qt3ds::render::NVRenderStencilFunctionArgument; +using qt3ds::render::NVRenderBoolOp; +using qt3ds::render::NVRenderStencilOperationArgument; +using qt3ds::render::NVRenderStencilOp; + +typedef qt3dsimp::SPathBuffer TImportPathBuffer; +using namespace qt3ds::render::path; + +typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair; + +namespace eastl { +template <> +struct hash<TStrStrPair> +{ + size_t operator()(const TStrStrPair &item) const + { + return eastl::hash<CRegisteredString>()(item.first) + ^ eastl::hash<CRegisteredString>()(item.second); + } +}; +} + +struct SPathShaderMapKey +{ + CRegisteredString m_Name; + SShaderDefaultMaterialKey m_MaterialKey; + size_t m_HashCode; + SPathShaderMapKey(CRegisteredString inName, SShaderDefaultMaterialKey inKey) + : m_Name(inName) + , m_MaterialKey(inKey) + { + m_HashCode = eastl::hash<TStrStrPair>()(m_Name) ^ m_MaterialKey.hash(); + } + bool operator==(const SPathShaderMapKey &inKey) const + { + return m_Name == inKey.m_Name && m_MaterialKey == inKey.m_MaterialKey; + } +}; + +namespace eastl { +template <> +struct hash<SPathShaderMapKey> +{ + size_t operator()(const SPathShaderMapKey &inKey) const { return inKey.m_HashCode; } +}; +} + +namespace { + +struct SPathSubPathBuffer +{ + NVAllocatorCallback &m_Allocator; + nvvector<SPathAnchorPoint> m_SourceData; + SPathDirtyFlags m_Flags; + SPathSubPath &m_SubPath; + bool m_Closed; + + QT3DSI32 m_RefCount; + + SPathSubPathBuffer(NVAllocatorCallback &alloc, SPathSubPath &inSubPath) + : m_Allocator(alloc) + , m_SourceData(alloc, "m_SourceData") + , m_SubPath(inSubPath) + , m_Closed(false) + , m_RefCount(0) + { + } + + void addRef() { atomicIncrement(&m_RefCount); } + void release() + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } +}; + +struct SImportPathWrapper +{ + NVAllocatorCallback &m_Alloc; + qt3dsimp::SPathBuffer *m_Path; + QT3DSI32 m_RefCount; + + SImportPathWrapper(NVAllocatorCallback &inAlloc, qt3dsimp::SPathBuffer &inPath) + : m_Alloc(inAlloc) + , m_Path(&inPath) + , m_RefCount(0) + { + } + + ~SImportPathWrapper() { m_Path->Free(m_Alloc); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Alloc); + NVDelete(alloc, this); + } + } +}; + +typedef NVScopedRefCounted<SImportPathWrapper> TPathBufferPtr; + +struct SPathBuffer +{ + NVAllocatorCallback &m_Allocator; + nvvector<NVScopedRefCounted<SPathSubPathBuffer>> m_SubPaths; + TPathBufferPtr m_PathBuffer; + + NVScopedRefCounted<NVRenderVertexBuffer> m_PatchData; + NVScopedRefCounted<NVRenderInputAssembler> m_InputAssembler; + NVScopedRefCounted<NVRenderPathRender> m_PathRender; + + QT3DSVec2 m_BeginTaperData; + QT3DSVec2 m_EndTaperData; + QT3DSU32 m_NumVertexes; + PathTypes::Enum m_PathType; + QT3DSF32 m_Width; + QT3DSF32 m_CPUError; + NVBounds3 m_Bounds; + Option<STaperInformation> m_BeginTaper; + Option<STaperInformation> m_EndTaper; + CRegisteredString m_SourcePath; + + // Cached data for geometry paths + + SPathDirtyFlags m_Flags; + + QT3DSI32 m_RefCount; + + SPathBuffer(NVAllocatorCallback &alloc) + : m_Allocator(alloc) + , m_SubPaths(alloc, "m_SubPaths") + , m_NumVertexes(0) + , m_PathType(PathTypes::Geometry) + , m_Width(0.0f) + , m_CPUError(0.0f) + , m_Bounds(NVBounds3::empty()) + , m_RefCount(0) + { + } + + void addRef() { atomicIncrement(&m_RefCount); } + void release() + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } + + void ClearGeometryPathData() + { + m_PatchData = NULL; + m_InputAssembler = NULL; + } + + void ClearPaintedPathData() { m_PathRender = NULL; } + + qt3dsimp::SPathBuffer GetPathData(qt3dsimp::IPathBufferBuilder &inSpec) + { + if (m_SubPaths.size()) { + inSpec.Clear(); + for (QT3DSU32 idx = 0, end = m_SubPaths.size(); idx < end; ++idx) { + const SPathSubPathBuffer &theSubPathBuffer(*m_SubPaths[idx]); + for (QT3DSU32 equationIdx = 0, equationEnd = theSubPathBuffer.m_SourceData.size(); + equationIdx < equationEnd; ++equationIdx) { + const SPathAnchorPoint &thePoint = theSubPathBuffer.m_SourceData[equationIdx]; + if (equationIdx == 0) { + inSpec.MoveTo(thePoint.m_Position); + } else { + const SPathAnchorPoint &thePrevPoint = + theSubPathBuffer.m_SourceData[equationIdx - 1]; + QT3DSVec2 c1 = IPathManager::GetControlPointFromAngleDistance( + thePrevPoint.m_Position, thePrevPoint.m_OutgoingAngle, + thePrevPoint.m_OutgoingDistance); + QT3DSVec2 c2 = IPathManager::GetControlPointFromAngleDistance( + thePoint.m_Position, thePoint.m_IncomingAngle, + thePoint.m_IncomingDistance); + QT3DSVec2 p2 = thePoint.m_Position; + inSpec.CubicCurveTo(c1, c2, p2); + } + } + if (theSubPathBuffer.m_Closed) + inSpec.Close(); + } + return inSpec.GetPathBuffer(); + } else if (m_PathBuffer.mPtr) + return *m_PathBuffer.mPtr->m_Path; + return qt3dsimp::SPathBuffer(); + } + + void SetPathType(PathTypes::Enum inPathType) + { + if (inPathType != m_PathType) { + switch (m_PathType) { + case PathTypes::Geometry: + ClearGeometryPathData(); + break; + case PathTypes::Painted: + ClearPaintedPathData(); + break; + default: + QT3DS_ALWAYS_ASSERT_MESSAGE("Unexpected path type"); + // No further processing for unexpected path type + return; + } + m_Flags.clearOrSet(true, PathDirtyFlagValues::PathType); + } + m_PathType = inPathType; + } + + static Option<STaperInformation> ToTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset, + QT3DSF32 capOpacity, QT3DSF32 capWidth) + { + if (capping == PathCapping::Noner) + return Empty(); + + return STaperInformation(capOffset, capOpacity, capWidth); + } + + void SetBeginTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset, QT3DSF32 capOpacity, + QT3DSF32 capWidth) + { + Option<STaperInformation> newBeginInfo = + ToTaperInfo(capping, capOffset, capOpacity, capWidth); + if (!OptionEquals(newBeginInfo, m_BeginTaper)) { + m_BeginTaper = newBeginInfo; + m_Flags.clearOrSet(true, PathDirtyFlagValues::BeginTaper); + } + } + + void SetEndTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset, QT3DSF32 capOpacity, + QT3DSF32 capWidth) + { + Option<STaperInformation> newEndInfo = + ToTaperInfo(capping, capOffset, capOpacity, capWidth); + if (!OptionEquals(newEndInfo, m_EndTaper)) { + m_EndTaper = newEndInfo; + m_Flags.clearOrSet(true, PathDirtyFlagValues::EndTaper); + } + } + + void SetWidth(QT3DSF32 inWidth) + { + if (inWidth != m_Width) { + m_Width = inWidth; + m_Flags.clearOrSet(true, PathDirtyFlagValues::Width); + } + } + + void SetCPUError(QT3DSF32 inError) + { + if (inError != m_CPUError) { + m_CPUError = inError; + m_Flags.clearOrSet(true, PathDirtyFlagValues::CPUError); + } + } +}; + +struct SPathGeneratedShader +{ + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSF32> m_Width; + NVRenderCachedShaderProperty<QT3DSF32> m_InnerTessAmount; + NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessAmount; + NVRenderCachedShaderProperty<QT3DSVec2> m_BeginTaperData; + NVRenderCachedShaderProperty<QT3DSVec2> m_EndTaperData; + NVRenderCachedShaderProperty<QT3DSMat44> m_WireframeViewMatrix; + + QT3DSI32 m_RefCount; + + SPathGeneratedShader(NVRenderShaderProgram &sh, NVAllocatorCallback &alloc) + : m_Allocator(alloc) + , m_Shader(sh) + , m_Width("pathWidth", sh) + , m_InnerTessAmount("tessInnerLevel", sh) + , m_EdgeTessAmount("tessEdgeLevel", sh) + , m_BeginTaperData("beginTaperInfo", sh) + , m_EndTaperData("endTaperInfo", sh) + , m_WireframeViewMatrix("viewport_matrix", sh) + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~SPathGeneratedShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &allocator(m_Allocator); + NVDelete(allocator, this); + } + } +}; + +struct SPathVertexPipeline : public SVertexPipelineImpl +{ + + SPathVertexPipeline(IShaderProgramGenerator &inProgGenerator, + IMaterialShaderGenerator &inMaterialGenerator, NVAllocatorCallback &inAlloc, + IStringTable &inStringTable, bool inWireframe) + : SVertexPipelineImpl(inAlloc, inMaterialGenerator, inProgGenerator, inStringTable, + inWireframe) + { + } + + // Trues true if the code was *not* set. + bool SetCode(GenerationFlagValues::Enum inCode) + { + if (((QT3DSU32)m_GenerationFlags & inCode) != 0) + return true; + m_GenerationFlags |= inCode; + return false; + } + + void AssignTessEvalVarying(const char8_t *inVarName, const char8_t *inVarValueExpr) + { + const char8_t *ext = ""; + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + ext = "TE"; + TessEval() << "\t" << inVarName << ext << " = " << inVarValueExpr << ";" << Endl; + } + + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValueExpr) override + { + AssignTessEvalVarying(inVarName, inVarValueExpr); + } + + void InitializeTessShaders() + { + IShaderStageGenerator &theTessControl(TessControl()); + IShaderStageGenerator &theTessEval(TessEval()); + + // first setup tessellation control shader + theTessControl.AddUniform("tessEdgeLevel", "float"); + theTessControl.AddUniform("tessInnerLevel", "float"); + + theTessControl.AddInclude("tessellationPath.glsllib"); + + theTessControl.Append("void main() {\n"); + theTessControl.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + theTessControl.Append("\ttessShader( tessEdgeLevel, tessInnerLevel );\n"); + + bool hasGeometryShader = + ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) != NULL; + + // second setup tessellation control shader + eastl::string outExt(""); + if (hasGeometryShader) + outExt = "TE"; + + theTessEval.AddInclude("tessellationPath.glsllib"); + theTessEval.AddUniform("normal_matrix", "mat3"); + theTessEval.AddUniform("model_view_projection", "mat4"); + theTessEval.AddUniform("pathWidth", "float"); + theTessEval.AddUniform("material_diffuse", "vec4"); + AddInterpolationParameter("varTexCoord0", "vec2"); + AddInterpolationParameter("varTessOpacity", "float"); + + theTessEval.Append("void main() {\n"); + theTessEval.Append("\tSTessShaderResult shaderResult = tessShader( pathWidth );\n"); + theTessEval.Append("\tvec3 pos = shaderResult.m_Position;\n"); + AssignTessEvalVarying("varTessOpacity", "shaderResult.m_Opacity"); + AssignTessEvalVarying("varTexCoord0", "shaderResult.m_TexCoord.xy"); + if (hasGeometryShader) + theTessEval << "\tvec2 varTexCoord0 = shaderResult.m_TexCoord.xy;\n"; + + theTessEval << "\tvec3 object_normal = vec3(0.0, 0.0, 1.0);\n"; + theTessEval << "\tvec3 world_normal = normal_matrix * object_normal;\n"; + theTessEval << "\tvec3 tangent = vec3( shaderResult.m_Tangent, 0.0 );\n"; + theTessEval << "\tvec3 binormal = vec3( shaderResult.m_Binormal, 0.0 );\n"; + + // These are necessary for texture generation. + theTessEval << "\tvec3 uTransform;" << Endl; + theTessEval << "\tvec3 vTransform;" << Endl; + + if (m_DisplacementImage) { + MaterialGenerator().GenerateImageUVCoordinates(*this, m_DisplacementIdx, 0, + *m_DisplacementImage); + theTessEval.AddUniform("displaceAmount", "float"); + theTessEval.AddUniform("model_matrix", "mat4"); + theTessEval.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + IDefaultMaterialShaderGenerator::SImageVariableNames theVarNames = + MaterialGenerator().GetImageVariableNames(m_DisplacementIdx); + + theTessEval.AddUniform(theVarNames.m_ImageSampler, "sampler2D"); + IDefaultMaterialShaderGenerator::SImageVariableNames theNames = + MaterialGenerator().GetImageVariableNames(m_DisplacementIdx); + theTessEval << "\tpos = defaultMaterialFileDisplacementTexture( " + << theNames.m_ImageSampler << ", displaceAmount, " + << theNames.m_ImageFragCoords << outExt.c_str() << ", vec3( 0.0, 0.0, 1.0 )" + << ", pos.xyz );" << Endl; + } + } + void FinalizeTessControlShader() {} + + void FinalizeTessEvaluationShader() + { + eastl::string outExt(""); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + outExt = "TE"; + + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + tessEvalShader.Append("\tgl_Position = model_view_projection * vec4( pos, 1.0 );\n"); + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + SetupDisplacement(displacementImageIdx, displacementImage); + + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + theStages |= ShaderGeneratorStages::TessControl; + theStages |= ShaderGeneratorStages::TessEval; + if (m_Wireframe) { + theStages |= ShaderGeneratorStages::Geometry; + } + ProgramGenerator().BeginProgram(theStages); + InitializeTessShaders(); + if (m_Wireframe) { + InitializeWireframeGeometryShader(); + } + // Open up each stage. + IShaderStageGenerator &vertexShader(Vertex()); + + vertexShader.AddIncoming("attr_pos", "vec4"); + + // useless vert shader because real work is done in TES. + vertexShader << "void main()\n" + "{\n"; + vertexShader << "\tgl_Position = attr_pos;\n"; // if tessellation is enabled pass down + // object coordinates; + vertexShader << "}\n"; + } + + void BeginFragmentGeneration() override + { + Fragment().AddUniform("material_diffuse", "vec4"); + Fragment() << "void main()" << Endl << "{" << Endl; + // We do not pass object opacity through the pipeline. + Fragment() << "\tfloat object_opacity = varTessOpacity * material_diffuse.a;" << Endl; + } + void DoGenerateUVCoords(QT3DSU32) override + { + // these are always generated regardless + } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void DoGenerateWorldNormal() override { AssignTessEvalVarying("varNormal", "world_normal"); } + void DoGenerateObjectNormal() override + { + AssignTessEvalVarying("varObjectNormal", "object_normal"); + } + void DoGenerateWorldPosition() override + { + TessEval().AddUniform("model_matrix", "mat4"); + TessEval() + << "\tvec3 local_model_world_position = vec3((model_matrix * vec4(pos, 1.0)).xyz);\n"; + } + void DoGenerateVarTangentAndBinormal() override + { + TessEval().AddUniform("normal_matrix", "mat3"); + AssignOutput("varTangent", "normal_matrix * tangent"); + AssignOutput("varBinormal", "normal_matrix * binormal"); + } + + void DoGenerateVertexColor() override + { + Vertex().AddIncoming("attr_color", "vec3"); + Vertex() << "\tvarColor = attr_color;" << Endl; + } + + void EndVertexGeneration(bool) override + { + + if (HasTessellation()) { + // finalize tess control shader + FinalizeTessControlShader(); + // finalize tess evaluation shader + FinalizeTessEvaluationShader(); + + TessControl().Append("}"); + TessEval().Append("}"); + } + if (m_Wireframe) { + // finalize geometry shader + FinalizeWireframeGeometryShader(); + Geometry().Append("}"); + } + } + + void EndFragmentGeneration(bool) override { Fragment().Append("}"); } + + void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override + { + m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType))); + Fragment().AddIncoming(inName, inType); + if (HasTessellation()) { + eastl::string nameBuilder; + nameBuilder.assign(inName); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + nameBuilder.append("TE"); + + TessEval().AddOutgoing(nameBuilder.c_str(), inType); + } + } + + IShaderStageGenerator &ActiveStage() override { return TessEval(); } +}; + +struct SPathXYGeneratedShader +{ + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSVec4> m_RectDimensions; + NVRenderCachedShaderProperty<QT3DSMat44> m_ModelMatrix; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + QT3DSI32 m_RefCount; + + SPathXYGeneratedShader(NVRenderShaderProgram &sh, NVAllocatorCallback &alloc) + : m_Allocator(alloc) + , m_Shader(sh) + , m_RectDimensions("uni_rect_dimensions", sh) + , m_ModelMatrix("model_matrix", sh) + , m_CameraPosition("camera_position", sh) + , m_CameraProperties("camera_properties", sh) + , m_RefCount(0) + { + m_Shader.addRef(); + } + virtual ~SPathXYGeneratedShader() { m_Shader.release(); } + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &allocator(m_Allocator); + NVDelete(allocator, this); + } + } +}; + +// Helper implements the vertex pipeline for mesh subsets when bound to the default material. +// Should be completely possible to use for custom materials with a bit of refactoring. +struct SXYRectVertexPipeline : public SVertexPipelineImpl +{ + + SXYRectVertexPipeline(IShaderProgramGenerator &inProgGenerator, + IMaterialShaderGenerator &inMaterialGenerator, + NVAllocatorCallback &inAlloc, IStringTable &inStringTable) + : SVertexPipelineImpl(inAlloc, inMaterialGenerator, inProgGenerator, inStringTable, false) + { + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + ProgramGenerator().BeginProgram(theStages); + // Open up each stage. + IShaderStageGenerator &vertexShader(Vertex()); + vertexShader.AddIncoming("attr_pos", "vec2"); + vertexShader.AddUniform("uni_rect_dimensions", "vec4"); + + vertexShader << "void main()" << Endl << "{" << Endl; + vertexShader << "\tvec3 uTransform;" << Endl; + vertexShader << "\tvec3 vTransform;" << Endl; + + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader + << "\tfloat posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );" + << Endl; + vertexShader + << "\tfloat posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );" + << Endl; + vertexShader << "\tvec3 pos = vec3(posX, posY, 0.0 );" << Endl; + vertexShader.Append("\tgl_Position = model_view_projection * vec4(pos, 1.0);"); + } + + void OutputParaboloidDepthShaders() + { + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + ProgramGenerator().BeginProgram(theStages); + IShaderStageGenerator &vertexShader(Vertex()); + vertexShader.AddIncoming("attr_pos", "vec2"); + vertexShader.AddUniform("uni_rect_dimensions", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader << "void main()" << Endl << "{" << Endl; + vertexShader + << "\tfloat posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );" + << Endl; + vertexShader + << "\tfloat posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );" + << Endl; + vertexShader << "\tvec3 pos = vec3(posX, posY, 0.0 );" << Endl; + IShaderProgramGenerator::OutputParaboloidDepthTessEval(vertexShader); + vertexShader << "}" << Endl; + + IShaderProgramGenerator::OutputParaboloidDepthFragment(Fragment()); + } + + void OutputCubeFaceDepthShaders() + { + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + ProgramGenerator().BeginProgram(theStages); + IShaderStageGenerator &vertexShader(Vertex()); + IShaderStageGenerator &fragmentShader(Fragment()); + vertexShader.AddIncoming("attr_pos", "vec2"); + vertexShader.AddUniform("uni_rect_dimensions", "vec4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.Append("void main() {"); + vertexShader.Append( + " float posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );"); + vertexShader.Append( + " float posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );"); + vertexShader.Append(" world_pos = model_matrix * vec4( posX, posY, 0.0, 1.0 );"); + vertexShader.Append(" world_pos /= world_pos.w;"); + vertexShader.Append( + " gl_Position = model_view_projection * vec4( posX, posY, 0.0, 1.0 );"); + vertexShader.Append("}"); + + fragmentShader.AddUniform("camera_position", "vec3"); + fragmentShader.AddUniform("camera_properties", "vec2"); + + BeginFragmentGeneration(); + fragmentShader.Append( + "\tfloat dist = 0.5 * length( world_pos.xyz - camera_position );"); // Why? + fragmentShader.Append( + "\tdist = (dist - camera_properties.x) / (camera_properties.y - camera_properties.x);"); + fragmentShader.Append("\tfragOutput = vec4(dist);"); + fragmentShader.Append("}"); + } + + void BeginFragmentGeneration() override + { + Fragment().AddUniform("material_diffuse", "vec4"); + Fragment() << "void main()" << Endl << "{" << Endl; + // We do not pass object opacity through the pipeline. + Fragment() << "\tfloat object_opacity = material_diffuse.a;" << Endl; + } + + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValue) override + { + Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n"; + } + void DoGenerateUVCoords(QT3DSU32) override { Vertex() << "\tvarTexCoord0 = attr_pos;" << Endl; } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void DoGenerateWorldNormal() override + { + IShaderStageGenerator &vertexGenerator(Vertex()); + vertexGenerator.AddUniform("normal_matrix", "mat3"); + vertexGenerator.Append( + "\tvec3 world_normal = normalize(normal_matrix * vec3( 0.0, 0.0, 1.0) ).xyz;"); + vertexGenerator.Append("\tvarNormal = world_normal;"); + } + + void DoGenerateObjectNormal() override + { + AddInterpolationParameter("varObjectNormal", "vec3"); + Vertex().Append("\tvarObjectNormal = vec3(0.0, 0.0, 1.0 );"); + } + + void DoGenerateWorldPosition() override + { + Vertex().Append("\tvec3 local_model_world_position = (model_matrix * vec4(pos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + + void DoGenerateVarTangentAndBinormal() override + { + Vertex().AddIncoming("attr_textan", "vec3"); + Vertex().AddIncoming("attr_binormal", "vec3"); + Vertex() << "\tvarTangent = normal_matrix * vec3(1.0, 0.0, 0.0);" << Endl + << "\tvarBinormal = normal_matrix * vec3(0.0, 1.0, 0.0);" << Endl; + } + + void DoGenerateVertexColor() override + { + Vertex().AddIncoming("attr_color", "vec3"); + Vertex() << "\tvarColor = attr_color;" << Endl; + } + + void EndVertexGeneration(bool) override { Vertex().Append("}"); } + + void EndFragmentGeneration(bool) override { Fragment().Append("}"); } + + void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override + { + m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType))); + Vertex().AddOutgoing(inName, inType); + Fragment().AddIncoming(inName, inType); + } + + IShaderStageGenerator &ActiveStage() override { return Vertex(); } +}; + +struct SPathManager : public IPathManager +{ + typedef nvhash_map<SPath *, NVScopedRefCounted<SPathBuffer>> TPathBufferHash; + typedef nvhash_map<SPathSubPath *, NVScopedRefCounted<SPathSubPathBuffer>> + TPathSubPathBufferHash; + typedef nvhash_map<SPathShaderMapKey, NVScopedRefCounted<SPathGeneratedShader>> TShaderMap; + typedef nvhash_map<SPathShaderMapKey, NVScopedRefCounted<SPathXYGeneratedShader>> + TPaintedShaderMap; + typedef nvhash_map<CRegisteredString, TPathBufferPtr> TStringPathBufferMap; + + IQt3DSRenderContextCore &m_CoreContext; + IQt3DSRenderContext *m_RenderContext; + eastl::string m_IdBuilder; + TPathSubPathBufferHash m_SubPathBuffers; + TPathBufferHash m_Buffers; + nvvector<SResultCubic> m_SubdivResult; + nvvector<QT3DSF32> m_KeyPointVec; + nvvector<QT3DSVec4> m_PatchBuffer; + TShaderMap m_PathGeometryShaders; + TPaintedShaderMap m_PathPaintedShaders; + TStringPathBufferMap m_SourcePathBufferMap; + Mutex m_PathBufferMutex; + + NVScopedRefCounted<SPathGeneratedShader> m_DepthShader; + NVScopedRefCounted<SPathGeneratedShader> m_DepthDisplacementShader; + NVScopedRefCounted<SPathGeneratedShader> m_GeometryShadowShader; + NVScopedRefCounted<SPathGeneratedShader> m_GeometryCubeShadowShader; + NVScopedRefCounted<SPathGeneratedShader> m_GeometryDisplacementShadowShader; + + NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedDepthShader; + NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedShadowShader; + NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedCubeShadowShader; + NVScopedRefCounted<NVRenderInputAssembler> m_PaintedRectInputAssembler; + NVScopedRefCounted<NVRenderVertexBuffer> m_PaintedRectVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_PaintedRectIndexBuffer; + + nvvector<NVScopedRefCounted<NVRenderDepthStencilState>> m_DepthStencilStates; + + NVScopedRefCounted<NVRenderPathSpecification> m_PathSpecification; + NVScopedRefCounted<qt3dsimp::IPathBufferBuilder> m_PathBuilder; + + QT3DSI32 m_RefCount; + + SPathManager(IQt3DSRenderContextCore &inRC) + : m_CoreContext(inRC) + , m_RenderContext(NULL) + , m_SubPathBuffers(inRC.GetAllocator(), "m_SubPathBuffers") + , m_Buffers(inRC.GetAllocator(), "m_Buffers") + , m_SubdivResult(inRC.GetAllocator(), "m_SubdivResult") + , m_KeyPointVec(inRC.GetAllocator(), "m_KeyPointVec") + , m_PatchBuffer(inRC.GetAllocator(), "m_QuadStrip") + , m_PathGeometryShaders(inRC.GetAllocator(), "m_PathGeometryShaders") + , m_PathPaintedShaders(inRC.GetAllocator(), "m_PathPaintedShaders") + , m_SourcePathBufferMap(inRC.GetAllocator(), "m_SourcePathBufferMap") + , m_PathBufferMutex(inRC.GetAllocator()) + , m_DepthStencilStates(inRC.GetAllocator(), "m_DepthStencilStates") + , m_RefCount(0) + { + } + + virtual ~SPathManager() { m_PaintedRectInputAssembler = NULL; } + + NVAllocatorCallback &GetAllocator() { return m_CoreContext.GetAllocator(); } + IStringTable &GetStringTable() { return m_CoreContext.GetStringTable(); } + NVFoundationBase &GetFoundation() { return m_CoreContext.GetFoundation(); } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(GetAllocator()); + NVDelete(alloc, this); + } + } + // Called during binary load which is heavily threaded. + void SetPathSubPathData(const SPathSubPath &inPath, + NVConstDataRef<SPathAnchorPoint> inPathCubicCurves) override + { + Mutex::ScopedLock __locker(m_PathBufferMutex); + eastl::pair<TPathSubPathBufferHash::iterator, bool> inserter = + m_SubPathBuffers.insert(eastl::make_pair((SPathSubPath *)&inPath, + NVScopedRefCounted<SPathSubPathBuffer>(NULL))); + if (!inserter.first->second) + inserter.first->second = QT3DS_NEW(GetAllocator(), SPathSubPathBuffer)( + GetAllocator(), const_cast<SPathSubPath &>(inPath)); + SPathSubPathBuffer &theBuffer = *inserter.first->second.mPtr; + theBuffer.m_SourceData.assign(inPathCubicCurves.begin(), inPathCubicCurves.end()); + theBuffer.m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + } + + SPathBuffer *GetPathBufferObject(const SPath &inPath) + { + eastl::pair<TPathBufferHash::iterator, bool> inserter = m_Buffers.insert( + eastl::make_pair((SPath *)&inPath, NVScopedRefCounted<SPathBuffer>(NULL))); + if (inserter.second) { + inserter.first->second = QT3DS_NEW(GetAllocator(), SPathBuffer)(GetAllocator()); + } + return inserter.first->second.mPtr; + } + + SPathSubPathBuffer *GetPathBufferObject(const SPathSubPath &inSubPath) + { + TPathSubPathBufferHash::iterator iter = m_SubPathBuffers.find((SPathSubPath *)&inSubPath); + if (iter != m_SubPathBuffers.end()) + return iter->second.mPtr; + return NULL; + } + + NVDataRef<SPathAnchorPoint> GetPathSubPathBuffer(const SPathSubPath &inPath) override + { + SPathSubPathBuffer *theBuffer = GetPathBufferObject(inPath); + if (theBuffer) + return toDataRef(theBuffer->m_SourceData.data(), (QT3DSU32)theBuffer->m_SourceData.size()); + return NVDataRef<SPathAnchorPoint>(); + } + + NVDataRef<SPathAnchorPoint> ResizePathSubPathBuffer(const SPathSubPath &inPath, + QT3DSU32 inNumAnchors) override + { + SPathSubPathBuffer *theBuffer = GetPathBufferObject(inPath); + if (theBuffer == NULL) + SetPathSubPathData(inPath, NVConstDataRef<SPathAnchorPoint>()); + theBuffer = GetPathBufferObject(inPath); + theBuffer->m_SourceData.resize(inNumAnchors); + theBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + return toDataRef(theBuffer->m_SourceData.data(), (QT3DSU32)theBuffer->m_SourceData.size()); + } + + // This needs to be done using roots of the first derivative. + NVBounds3 GetBounds(const SPath &inPath) override + { + NVBounds3 retval(NVBounds3::empty()); + + SPathBuffer *thePathBuffer = GetPathBufferObject(inPath); + if (thePathBuffer) { + SPathDirtyFlags geomDirtyFlags( + PathDirtyFlagValues::SourceData | PathDirtyFlagValues::BeginTaper + | PathDirtyFlagValues::EndTaper | PathDirtyFlagValues::Width + | PathDirtyFlagValues::CPUError); + + if ((((QT3DSU32)thePathBuffer->m_Flags) & (QT3DSU32)geomDirtyFlags) == 0) { + return thePathBuffer->m_Bounds; + } + } + + for (SPathSubPath *theSubPath = inPath.m_FirstSubPath; theSubPath; + theSubPath = theSubPath->m_NextSubPath) { + SPathSubPathBuffer *theBuffer = GetPathBufferObject(*theSubPath); + if (!theBuffer) + continue; + + QT3DSU32 numAnchors = theBuffer->m_SourceData.size(); + for (QT3DSU32 idx = 0, end = numAnchors; idx < end; ++idx) { + const SPathAnchorPoint &thePoint(theBuffer->m_SourceData[idx]); + QT3DSVec2 position(thePoint.m_Position); + retval.include(QT3DSVec3(position.x, position.y, 0.0f)); + if (idx) { + QT3DSVec2 incoming(IPathManagerCore::GetControlPointFromAngleDistance( + thePoint.m_Position, thePoint.m_IncomingAngle, + thePoint.m_IncomingDistance)); + retval.include(QT3DSVec3(incoming.x, incoming.y, 0.0f)); + } + + if (idx < (numAnchors - 1)) { + QT3DSVec2 outgoing(IPathManagerCore::GetControlPointFromAngleDistance( + thePoint.m_Position, thePoint.m_OutgoingAngle, + thePoint.m_OutgoingDistance)); + retval.include(QT3DSVec3(outgoing.x, outgoing.y, 0.0f)); + } + } + } + + return retval; + } + + IPathManager &OnRenderSystemInitialize(IQt3DSRenderContext &context) override + { + m_RenderContext = &context; + return *this; + } + + // find a point that will join these two curves *if* they are not first derivative continuous + static Option<QT3DSVec2> GetAdjoiningPoint(QT3DSVec2 prevC2, QT3DSVec2 point, QT3DSVec2 C1, QT3DSF32 pathWidth) + { + QT3DSVec2 incomingDxDy = (point - prevC2); + QT3DSVec2 outgoingDxDy = (C1 - point); + incomingDxDy.normalize(); + outgoingDxDy.normalize(); + float determinant = (incomingDxDy.x * outgoingDxDy.y) - (incomingDxDy.y * outgoingDxDy.x); + if (fabs(determinant) > .001f) { + float mult = determinant > 0.0f ? 1.0f : -1.0f; + QT3DSVec2 incomingNormal(incomingDxDy.y, -incomingDxDy.x); + QT3DSVec2 outgoingNormal(outgoingDxDy.y, -outgoingDxDy.x); + + QT3DSVec2 leftEdge = point + mult * incomingNormal * pathWidth; + QT3DSVec2 rightEdge = point + mult * outgoingNormal * pathWidth; + + return (leftEdge + rightEdge) / 2.0f; + } + return Empty(); + } + + Option<eastl::pair<QT3DSU32, QT3DSF32>> FindBreakEquation(QT3DSF32 inTaperStart) + { + QT3DSF32 lengthTotal = 0; + for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx) { + if (lengthTotal + m_SubdivResult[idx].m_Length > inTaperStart) { + QT3DSF32 breakTValue = (inTaperStart - lengthTotal) / m_SubdivResult[idx].m_Length; + nvvector<SResultCubic>::iterator breakIter = m_SubdivResult.begin() + idx; + SCubicBezierCurve theCurve(breakIter->m_P1, breakIter->m_C1, breakIter->m_C2, + breakIter->m_P2); + eastl::pair<SCubicBezierCurve, SCubicBezierCurve> subdivCurve = + theCurve.SplitCubicBezierCurve(breakTValue); + QT3DSF32 originalBreakT = + breakIter->m_TStart + (breakIter->m_TStop - breakIter->m_TStart) * breakTValue; + // Update the existing item to point to the second equation + breakIter->m_P1 = subdivCurve.second.m_Points[0]; + breakIter->m_C1 = subdivCurve.second.m_Points[1]; + breakIter->m_C2 = subdivCurve.second.m_Points[2]; + breakIter->m_P2 = subdivCurve.second.m_Points[3]; + QT3DSF32 originalLength = breakIter->m_Length; + QT3DSF32 originalStart = breakIter->m_TStart; + breakIter->m_Length *= (1.0f - breakTValue); + breakIter->m_TStart = originalBreakT; + SResultCubic newCubic(subdivCurve.first.m_Points[0], subdivCurve.first.m_Points[1], + subdivCurve.first.m_Points[2], subdivCurve.first.m_Points[3], + breakIter->m_EquationIndex, originalStart, originalBreakT, + originalLength * breakTValue); + + m_SubdivResult.insert(breakIter, newCubic); + return eastl::make_pair(idx, breakTValue); + } + lengthTotal += m_SubdivResult[idx].m_Length; + } + return Empty(); + } + + bool PrepareGeometryPathForRender(const SPath &inPath, SPathBuffer &inPathBuffer) + { + + m_SubdivResult.clear(); + m_KeyPointVec.clear(); + const SPath &thePath(inPath); + + inPathBuffer.SetBeginTaperInfo(thePath.m_BeginCapping, thePath.m_BeginCapOffset, + thePath.m_BeginCapOpacity, thePath.m_BeginCapWidth); + inPathBuffer.SetEndTaperInfo(thePath.m_EndCapping, thePath.m_EndCapOffset, + thePath.m_EndCapOpacity, thePath.m_EndCapWidth); + inPathBuffer.SetWidth(inPath.m_Width); + inPathBuffer.SetCPUError(inPath.m_LinearError); + + SPathDirtyFlags geomDirtyFlags(PathDirtyFlagValues::SourceData + | PathDirtyFlagValues::BeginTaper + | PathDirtyFlagValues::EndTaper | PathDirtyFlagValues::Width + | PathDirtyFlagValues::CPUError); + + bool retval = false; + if (!inPathBuffer.m_PatchData + || (((QT3DSU32)inPathBuffer.m_Flags) & (QT3DSU32)geomDirtyFlags) != 0) { + qt3dsimp::SPathBuffer thePathData = inPathBuffer.GetPathData(*m_PathBuilder); + + QT3DSU32 dataIdx = 0; + QT3DSVec2 prevPoint(0, 0); + QT3DSU32 equationIdx = 0; + for (QT3DSU32 commandIdx = 0, commandEnd = thePathData.m_Commands.size(); + commandIdx < commandEnd; ++commandIdx) { + switch (thePathData.m_Commands[commandIdx]) { + case qt3dsimp::PathCommand::MoveTo: + prevPoint = + QT3DSVec2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + break; + case qt3dsimp::PathCommand::CubicCurveTo: { + QT3DSVec2 c1(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + QT3DSVec2 c2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + QT3DSVec2 p2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + OuterAdaptiveSubdivideBezierCurve( + m_SubdivResult, m_KeyPointVec, SCubicBezierCurve(prevPoint, c1, c2, p2), + NVMax(inPath.m_LinearError, 1.0f), equationIdx); + ++equationIdx; + prevPoint = p2; + } break; + case qt3dsimp::PathCommand::Close: + break; + + default: + QT3DS_ASSERT(false); + break; + } + } + + QT3DSF32 theLocalWidth = inPath.m_Width / 2.0f; + + QT3DSVec2 theBeginTaperData(theLocalWidth, thePath.m_GlobalOpacity); + QT3DSVec2 theEndTaperData(theLocalWidth, thePath.m_GlobalOpacity); + + QT3DSF32 pathLength = 0.0f; + for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx) + pathLength += m_SubdivResult[idx].m_Length; + + if (thePath.m_BeginCapping == PathCapping::Taper + || thePath.m_EndCapping == PathCapping::Taper) { + QT3DSF32 maxTaperStart = pathLength / 2.0f; + if (thePath.m_BeginCapping == PathCapping::Taper) { + // Can't start more than halfway across the path. + QT3DSF32 taperStart = NVMin(thePath.m_BeginCapOffset, maxTaperStart); + QT3DSF32 endTaperWidth = thePath.m_BeginCapWidth; + QT3DSF32 endTaperOpacity = thePath.m_GlobalOpacity * thePath.m_BeginCapOpacity; + theBeginTaperData = QT3DSVec2(endTaperWidth, endTaperOpacity); + // Find where we need to break the current equations. + Option<eastl::pair<QT3DSU32, QT3DSF32>> breakEquationAndT( + FindBreakEquation(taperStart)); + if (breakEquationAndT.hasValue()) { + QT3DSU32 breakEquation = breakEquationAndT->first; + + QT3DSF32 lengthTotal = 0; + for (QT3DSU32 idx = 0, end = breakEquation; idx <= end; ++idx) { + SResultCubic &theCubic = m_SubdivResult[idx]; + theCubic.m_Mode = SResultCubic::BeginTaper; + + theCubic.m_TaperMultiplier[0] = lengthTotal / taperStart; + lengthTotal += theCubic.m_Length; + theCubic.m_TaperMultiplier[1] = lengthTotal / taperStart; + } + } + } + if (thePath.m_EndCapping == PathCapping::Taper) { + QT3DSF32 taperStart = NVMin(thePath.m_EndCapOffset, maxTaperStart); + QT3DSF32 endTaperWidth = thePath.m_EndCapWidth; + QT3DSF32 endTaperOpacity = thePath.m_GlobalOpacity * thePath.m_EndCapOpacity; + theEndTaperData = QT3DSVec2(endTaperWidth, endTaperOpacity); + // Invert taper start so that the forward search works. + Option<eastl::pair<QT3DSU32, QT3DSF32>> breakEquationAndT( + FindBreakEquation(pathLength - taperStart)); + + if (breakEquationAndT.hasValue()) { + QT3DSU32 breakEquation = breakEquationAndT->first; + ++breakEquation; + + QT3DSF32 lengthTotal = 0; + for (QT3DSU32 idx = breakEquation, end = m_SubdivResult.size(); idx < end; + ++idx) { + SResultCubic &theCubic = m_SubdivResult[idx]; + theCubic.m_Mode = SResultCubic::EndTaper; + + theCubic.m_TaperMultiplier[0] = 1.0f - (lengthTotal / taperStart); + lengthTotal += theCubic.m_Length; + theCubic.m_TaperMultiplier[1] = 1.0f - (lengthTotal / taperStart); + } + } + } + } + + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + // Create quads out of each point. + if (m_SubdivResult.empty()) + return false; + + // Generate patches. + m_PatchBuffer.clear(); + QT3DSF32 pathWidth = thePath.m_Width / 2.0f; + // texture coords + float texCoordU = 0.0; + + for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx) { + // create patches + SResultCubic thePoint(m_SubdivResult[idx]); + + m_PatchBuffer.push_back(CreateVec4(thePoint.m_P1, thePoint.m_C1)); + m_PatchBuffer.push_back(CreateVec4(thePoint.m_C2, thePoint.m_P2)); + + // Now we need to take care of cases where the control points of the adjoining + // SubPaths + // do not line up; i.e. there is a discontinuity of the 1st derivative + // The simplest way to do this is to move the edge vertex to a halfway point + // between a line bisecting the two control lines + QT3DSVec2 incomingAdjoining(thePoint.m_P1); + QT3DSVec2 outgoingAdjoining(thePoint.m_P2); + if (idx) { + SResultCubic previousCurve = m_SubdivResult[idx - 1]; + if (previousCurve.m_EquationIndex != thePoint.m_EquationIndex) { + QT3DSF32 anchorWidth = + thePoint.GetP1Width(pathWidth, theBeginTaperData.x, theEndTaperData.x); + Option<QT3DSVec2> adjoining = GetAdjoiningPoint( + previousCurve.m_C2, thePoint.m_P1, thePoint.m_C1, anchorWidth); + if (adjoining.hasValue()) + incomingAdjoining = *adjoining; + } + } + if (idx < (end - 1)) { + SResultCubic nextCurve = m_SubdivResult[idx + 1]; + if (nextCurve.m_EquationIndex != thePoint.m_EquationIndex) { + QT3DSF32 anchorWidth = + thePoint.GetP2Width(pathWidth, theBeginTaperData.x, theEndTaperData.x); + Option<QT3DSVec2> adjoining = GetAdjoiningPoint(thePoint.m_C2, thePoint.m_P2, + nextCurve.m_C1, anchorWidth); + if (adjoining.hasValue()) + outgoingAdjoining = *adjoining; + } + } + m_PatchBuffer.push_back(CreateVec4(incomingAdjoining, outgoingAdjoining)); + + QT3DSVec4 taperData(0.0f); + taperData.x = thePoint.m_TaperMultiplier.x; + taperData.y = thePoint.m_TaperMultiplier.y; + // Note we could put a *lot* more data into this thing. + taperData.z = (QT3DSF32)thePoint.m_Mode; + m_PatchBuffer.push_back(taperData); + + // texture coord generation + // note we only generate u here. v is generated in the tess shader + // u coord for P1 and C1 + QT3DSVec2 udata(texCoordU, texCoordU + (thePoint.m_Length / pathLength)); + texCoordU = udata.y; + m_PatchBuffer.push_back(QT3DSVec4(udata.x, udata.y, 0.0, 0.0)); + } + + // buffer size is 3.0*4.0*bufSize + QT3DSU32 bufSize = (QT3DSU32)m_PatchBuffer.size() * sizeof(QT3DSVec4); + QT3DSU32 stride = sizeof(QT3DSVec4); + + if ((!inPathBuffer.m_PatchData) || inPathBuffer.m_PatchData->Size() < bufSize) { + inPathBuffer.m_PatchData = theRenderContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, bufSize, stride, + toU8DataRef(m_PatchBuffer.data(), (QT3DSU32)m_PatchBuffer.size())); + inPathBuffer.m_NumVertexes = (QT3DSU32)m_PatchBuffer.size(); + inPathBuffer.m_InputAssembler = NULL; + } else { + QT3DS_ASSERT(inPathBuffer.m_PatchData->Size() >= bufSize); + inPathBuffer.m_PatchData->UpdateBuffer( + toU8DataRef(m_PatchBuffer.data(), (QT3DSU32)m_PatchBuffer.size())); + } + + if (!inPathBuffer.m_InputAssembler) { + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 4), + }; + + NVRenderDrawMode::Enum primType = NVRenderDrawMode::Patches; + + NVRenderAttribLayout *theLayout = + theRenderContext.CreateAttributeLayout(toConstDataRef(theEntries, 1)); + // How many vertices the TCS shader has access to in order to produce its output + // array of vertices. + const QT3DSU32 inputPatchVertexCount = 5; + inPathBuffer.m_InputAssembler = theRenderContext.CreateInputAssembler( + theLayout, toConstDataRef(inPathBuffer.m_PatchData.mPtr), NULL, + toConstDataRef(stride), toConstDataRef((QT3DSU32)0), primType, + inputPatchVertexCount); + } + inPathBuffer.m_BeginTaperData = theBeginTaperData; + inPathBuffer.m_EndTaperData = theEndTaperData; + + // cache bounds + NVBounds3 bounds = GetBounds(inPath); + inPathBuffer.m_Bounds.minimum = bounds.minimum; + inPathBuffer.m_Bounds.maximum = bounds.maximum; + } + + return retval; + } + + IMaterialShaderGenerator *GetMaterialShaderGenertator(SPathRenderContext &inRenderContext) + { + bool isDefaultMaterial = + (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial); + + IMaterialShaderGenerator *theMaterialGenerator = NULL; + if (isDefaultMaterial) + theMaterialGenerator = &m_RenderContext->GetDefaultMaterialShaderGenerator(); + else + theMaterialGenerator = &m_RenderContext->GetCustomMaterialShaderGenerator(); + + return theMaterialGenerator; + } + + CRegisteredString GetMaterialNameForKey(SPathRenderContext &inRenderContext) + { + bool isDefaultMaterial = + (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial); + + if (!isDefaultMaterial) { + ICustomMaterialSystem &theMaterialSystem(m_RenderContext->GetCustomMaterialSystem()); + const SCustomMaterial &theCustomMaterial( + reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material)); + + return m_RenderContext->GetStringTable().RegisterStr( + theMaterialSystem.GetShaderName(theCustomMaterial)); + } + + return m_RenderContext->GetStringTable().RegisterStr(""); + } + + bool PreparePaintedPathForRender(const SPath &inPath, SPathBuffer &inPathBuffer) + { + NVRenderContext &theContext(this->m_RenderContext->GetRenderContext()); + if (!inPathBuffer.m_PathRender + || (((QT3DSU32)inPathBuffer.m_Flags) & PathDirtyFlagValues::SourceData)) { + if (!inPathBuffer.m_PathRender) { + inPathBuffer.m_PathRender = theContext.CreatePathRender(); + } + + if (inPathBuffer.m_PathRender == NULL || m_PathSpecification == NULL) { + // QT3DS_ASSERT( false ); + return false; + } + + m_PathSpecification->Reset(); + qt3dsimp::SPathBuffer thePathData = inPathBuffer.GetPathData(*m_PathBuilder); + + QT3DSU32 dataIdx = 0; + for (QT3DSU32 commandIdx = 0, commandEnd = thePathData.m_Commands.size(); + commandIdx < commandEnd; ++commandIdx) { + + switch (thePathData.m_Commands[commandIdx]) { + case qt3dsimp::PathCommand::MoveTo: + m_PathSpecification->MoveTo( + QT3DSVec2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1])); + dataIdx += 2; + break; + case qt3dsimp::PathCommand::CubicCurveTo: { + QT3DSVec2 c1(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + QT3DSVec2 c2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + QT3DSVec2 p2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]); + dataIdx += 2; + m_PathSpecification->CubicCurveTo(c1, c2, p2); + } break; + case qt3dsimp::PathCommand::Close: + m_PathSpecification->ClosePath(); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + inPathBuffer.m_PathRender->SetPathSpecification(*m_PathSpecification); + + // cache bounds + NVBounds3 bounds = GetBounds(inPath); + inPathBuffer.m_Bounds.minimum = bounds.minimum; + inPathBuffer.m_Bounds.maximum = bounds.maximum; + + return true; + } + + return false; + } + + bool PrepareForRender(const SPath &inPath) override + { + SPathBuffer *thePathBuffer = GetPathBufferObject(inPath); + if (!thePathBuffer) { + return false; + } + NVRenderContext &theContext(this->m_RenderContext->GetRenderContext()); + if (!m_PathSpecification) + m_PathSpecification = theContext.CreatePathSpecification(); + if (!m_PathSpecification) + return false; + if (!m_PathBuilder) + m_PathBuilder = qt3dsimp::IPathBufferBuilder::CreateBuilder(GetFoundation()); + + thePathBuffer->SetPathType(inPath.m_PathType); + bool retval = false; + if (inPath.m_PathBuffer.IsValid() == false) { + thePathBuffer->m_PathBuffer = NULL; + // Ensure the SubPath list is identical and clear, percolating any dirty flags up to the + // path buffer. + QT3DSU32 SubPathIdx = 0; + for (const SPathSubPath *theSubPath = inPath.m_FirstSubPath; theSubPath; + theSubPath = theSubPath->m_NextSubPath, ++SubPathIdx) { + SPathSubPathBuffer *theSubPathBuffer = GetPathBufferObject(*theSubPath); + if (theSubPathBuffer == NULL) + continue; + thePathBuffer->m_Flags = + (QT3DSU32)(thePathBuffer->m_Flags | theSubPathBuffer->m_Flags); + + if (theSubPathBuffer->m_Closed != theSubPath->m_Closed) { + thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + theSubPathBuffer->m_Closed = theSubPath->m_Closed; + } + + if (thePathBuffer->m_SubPaths.size() <= SubPathIdx + || thePathBuffer->m_SubPaths[SubPathIdx] != theSubPathBuffer) { + thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + if (thePathBuffer->m_SubPaths.size() <= SubPathIdx) + thePathBuffer->m_SubPaths.push_back(theSubPathBuffer); + else + thePathBuffer->m_SubPaths[SubPathIdx] = theSubPathBuffer; + } + + theSubPathBuffer->m_Flags.Clear(); + } + + if (SubPathIdx != thePathBuffer->m_SubPaths.size()) { + thePathBuffer->m_SubPaths.resize(SubPathIdx); + thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + } + } else { + thePathBuffer->m_SubPaths.clear(); + eastl::pair<TStringPathBufferMap::iterator, bool> inserter = + m_SourcePathBufferMap.insert( + eastl::make_pair(inPath.m_PathBuffer, TPathBufferPtr())); + if (inserter.second) { + NVScopedRefCounted<IRefCountedInputStream> theStream = + m_CoreContext.GetInputStreamFactory().GetStreamForFile( + inPath.m_PathBuffer.c_str()); + if (theStream) { + qt3dsimp::SPathBuffer *theNewBuffer = + qt3dsimp::SPathBuffer::Load(*theStream, GetFoundation()); + if (theNewBuffer) + inserter.first->second = QT3DS_NEW(GetAllocator(), SImportPathWrapper)( + GetAllocator(), *theNewBuffer); + } + } + if (thePathBuffer->m_PathBuffer != inserter.first->second) { + thePathBuffer->m_PathBuffer = inserter.first->second; + thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData); + } + } + + if (inPath.m_PathType == PathTypes::Geometry) + retval = PrepareGeometryPathForRender(inPath, *thePathBuffer); + else + retval = PreparePaintedPathForRender(inPath, *thePathBuffer); + thePathBuffer->m_Flags.Clear(); + return retval; + } + + void SetMaterialProperties(NVRenderShaderProgram &inShader, SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties &inRenderProperties) + { + IMaterialShaderGenerator *theMaterialGenerator = + GetMaterialShaderGenertator(inRenderContext); + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + theRenderContext.SetActiveShader(&inShader); + + theMaterialGenerator->SetMaterialProperties( + inShader, inRenderContext.m_Material, inRenderContext.m_CameraVec, + inRenderContext.m_ModelViewProjection, inRenderContext.m_NormalMatrix, + inRenderContext.m_Path.m_GlobalTransform, inRenderContext.m_FirstImage, + inRenderContext.m_Opacity, inRenderProperties); + } + + void DoRenderGeometryPath(SPathGeneratedShader &inShader, SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties &inRenderProperties, + SPathBuffer &inPathBuffer) + { + if (inPathBuffer.m_InputAssembler == NULL) + return; + + SetMaterialProperties(inShader.m_Shader, inRenderContext, inRenderProperties); + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + + inShader.m_BeginTaperData.Set(inPathBuffer.m_BeginTaperData); + inShader.m_EndTaperData.Set(inPathBuffer.m_EndTaperData); + if (inRenderContext.m_EnableWireframe) { + // we need the viewport matrix + NVRenderRect theViewport(theRenderContext.GetViewport()); + QT3DSMat44 vpMatrix; + vpMatrix.column0 = QT3DSVec4((float)theViewport.m_Width / 2.0f, 0.0, 0.0, 0.0); + vpMatrix.column1 = QT3DSVec4(0.0, (float)theViewport.m_Height / 2.0f, 0.0, 0.0); + vpMatrix.column2 = QT3DSVec4(0.0, 0.0, 1.0, 0.0); + vpMatrix.column3 = + QT3DSVec4((float)theViewport.m_Width / 2.0f + (float)theViewport.m_X, + (float)theViewport.m_Height / 2.0f + (float)theViewport.m_Y, 0.0, 1.0); + + inShader.m_WireframeViewMatrix.Set(vpMatrix); + } + + QT3DSF32 tessEdgeValue = NVMin(64.0f, NVMax(1.0f, inRenderContext.m_Path.m_EdgeTessAmount)); + QT3DSF32 tessInnerValue = NVMin(64.0f, NVMax(1.0f, inRenderContext.m_Path.m_InnerTessAmount)); + inShader.m_EdgeTessAmount.Set(tessEdgeValue); + inShader.m_InnerTessAmount.Set(tessInnerValue); + inShader.m_Width.Set(inRenderContext.m_Path.m_Width / 2.0f); + theRenderContext.SetInputAssembler(inPathBuffer.m_InputAssembler); + theRenderContext.SetCullingEnabled(false); + NVRenderDrawMode::Enum primType = NVRenderDrawMode::Patches; + theRenderContext.Draw(primType, (QT3DSU32)inPathBuffer.m_NumVertexes, 0); + } + + NVRenderDepthStencilState *GetDepthStencilState() + { + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + NVRenderBoolOp::Enum theDepthFunction = theRenderContext.GetDepthFunction(); + bool isDepthEnabled = theRenderContext.IsDepthTestEnabled(); + bool isStencilEnabled = theRenderContext.IsStencilTestEnabled(); + bool isDepthWriteEnabled = theRenderContext.IsDepthWriteEnabled(); + for (QT3DSU32 idx = 0, end = m_DepthStencilStates.size(); idx < end; ++idx) { + NVRenderDepthStencilState &theState = *m_DepthStencilStates[idx]; + if (theState.GetDepthFunc() == theDepthFunction + && theState.GetDepthEnabled() == isDepthEnabled + && theState.GetDepthMask() == isDepthWriteEnabled) + return &theState; + } + NVRenderStencilFunctionArgument theArg(NVRenderBoolOp::NotEqual, 0, 0xFF); + NVRenderStencilOperationArgument theOpArg(NVRenderStencilOp::Keep, NVRenderStencilOp::Keep, + NVRenderStencilOp::Zero); + m_DepthStencilStates.push_back(theRenderContext.CreateDepthStencilState( + isDepthEnabled, isDepthWriteEnabled, theDepthFunction, isStencilEnabled, theArg, theArg, + theOpArg, theOpArg)); + return m_DepthStencilStates.back(); + } + + static void DoSetCorrectiveScale(const QT3DSMat44 &mvp, QT3DSMat44 &outScale, NVBounds3 pathBounds) + { + // Compute the projected locations for the paraboloid and regular projection + // and thereby set the appropriate scaling factor. + QT3DSVec3 points[4]; + QT3DSVec3 projReg[4], projParab[4]; + points[0] = pathBounds.minimum; + points[1] = QT3DSVec3(pathBounds.maximum.x, pathBounds.minimum.y, pathBounds.minimum.z); + points[2] = pathBounds.maximum; + points[3] = QT3DSVec3(pathBounds.minimum.x, pathBounds.maximum.y, pathBounds.maximum.z); + + // Do the two different projections. + for (int i = 0; i < 4; ++i) { + QT3DSVec4 tmp; + tmp = mvp.transform(QT3DSVec4(points[i], 1.0f)); + tmp /= tmp.w; + projReg[i] = tmp.getXYZ(); + projParab[i] = tmp.getXYZ().getNormalized(); + projParab[i] /= projParab[i].z + 1.0f; + } + + NVBounds3 boundsA, boundsB; + for (int i = 0; i < 4; ++i) { + boundsA.include(projReg[i]); + boundsB.include(projParab[i]); + } + QT3DSF32 xscale = + (boundsB.maximum.x - boundsB.minimum.x) / (boundsA.maximum.x - boundsA.minimum.x); + QT3DSF32 yscale = + (boundsB.maximum.y - boundsB.minimum.y) / (boundsA.maximum.y - boundsA.minimum.y); + QT3DSF32 zscale = (boundsB.maximum - boundsB.minimum).magnitudeSquared() + / (boundsA.maximum - boundsA.minimum).magnitudeSquared(); + // The default minimum here is just a stupid figure that looks good on our content because + // we'd + // been using it for a little while before. Just for demo. + xscale = NVMin<QT3DSF32>(0.5333333f, NVMin<QT3DSF32>(xscale, yscale)); + yscale = NVMin<QT3DSF32>(0.5333333f, NVMin<QT3DSF32>(xscale, yscale)); + outScale.scale(QT3DSVec4(xscale, yscale, zscale, 1.0f)); + } + + void DoRenderPaintedPath(SPathXYGeneratedShader &inShader, SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties &inRenderProperties, + SPathBuffer &inPathBuffer, bool isParaboloidPass = false) + { + if (!inPathBuffer.m_PathRender) + return; + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + if (!m_PaintedRectInputAssembler) { + QT3DSVec2 vertexes[] = { + QT3DSVec2(0.0, 0.0), QT3DSVec2(1.0, 0.0), QT3DSVec2(1.0, 1.0), QT3DSVec2(0.0, 1.0), + }; + + QT3DSU8 indexes[] = { + 0, 1, 2, 2, 3, 0, + }; + + QT3DSU32 stride = sizeof(QT3DSVec2); + + NVRenderVertexBufferEntry theBufferEntries[] = { NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0) }; + + m_PaintedRectVertexBuffer = theRenderContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, 4 * sizeof(QT3DSVec2), sizeof(QT3DSVec2), + toU8DataRef(vertexes, 4)); + m_PaintedRectIndexBuffer = theRenderContext.CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, + qt3ds::render::NVRenderComponentTypes::QT3DSU8, 6, toU8DataRef(indexes, 6)); + NVRenderAttribLayout *theAttribLayout = + theRenderContext.CreateAttributeLayout(toConstDataRef(theBufferEntries, 1)); + m_PaintedRectInputAssembler = theRenderContext.CreateInputAssembler( + theAttribLayout, toConstDataRef(m_PaintedRectVertexBuffer.mPtr), + m_PaintedRectIndexBuffer.mPtr, toConstDataRef(stride), toConstDataRef((QT3DSU32)0), + qt3ds::render::NVRenderDrawMode::Triangles); + } + + // our current render target needs stencil + QT3DS_ASSERT(theRenderContext.GetStencilBits() > 0); + + theRenderContext.SetDepthStencilState(GetDepthStencilState()); + + // http://developer.download.nvidia.com/assets/gamedev/files/Mixing_Path_Rendering_and_3D.pdf + theRenderContext.SetPathStencilDepthOffset(-.05f, -1.0f); + + // Stencil out the geometry. + QT3DSMat44 pathMdlView = QT3DSMat44::createIdentity(); + // Why is this happening? Well, it's because the painted-on path rendering is always + // a flat splatted 2D object. This is bad because a paraboloid projection demands a very + // different + // non-linear space into which we must draw. Path Rendering does not allow this sort of + // spatial + // warping internally, and all we end up passing in as a simple perspective projection. + // So for the fix, I'm scaling the actual "object" size so that it fits into the correctly + // projected + // polygon inside the paraboloid depth pass. Obviously, this scaling factor is wrong, and + // not generic + // enough to cover cases like polygons covering a large spread of the FOV and so on. It's + // really + // just a filthy awful, morally deplorable HACK. But it's basically the quickest fix at + // hand. + // This is also about the only possible approach that *could* work short of rendering the + // paths in + // a render-to-texture pass and splatting that texture on a sufficiently tessellated quad. + // Unless + // there's a way to program NVPR's internal projection scheme, that is. + // Geometry-based paths will work out better, I think, because they're actually creating + // geometry. + // This is essentially a 2D painting process inside a quad where the actual rendered region + // isn't + // exactly where NVPR thinks it should be because they're not projecting points the same + // way. + if (isParaboloidPass) { + DoSetCorrectiveScale(inRenderContext.m_ModelViewProjection, pathMdlView, + inPathBuffer.m_PathRender->GetPathObjectStrokeBox()); + } + + bool isStencilEnabled = theRenderContext.IsStencilTestEnabled(); + theRenderContext.SetStencilTestEnabled(true); + theRenderContext.SetPathProjectionMatrix(inRenderContext.m_ModelViewProjection); + theRenderContext.SetPathModelViewMatrix(pathMdlView); + + if (inRenderContext.m_IsStroke) { + inPathBuffer.m_PathRender->SetStrokeWidth(inRenderContext.m_Path.m_Width); + inPathBuffer.m_PathRender->StencilStroke(); + } else + inPathBuffer.m_PathRender->StencilFill(); + + // The stencil buffer will dictate whether this object renders or not. So we need to ignore + // the depth test result. + NVRenderBoolOp::Enum theDepthFunc = theRenderContext.GetDepthFunction(); + theRenderContext.SetDepthFunction(NVRenderBoolOp::AlwaysTrue); + // Now render the path; this resets the stencil buffer. + SetMaterialProperties(inShader.m_Shader, inRenderContext, inRenderProperties); + NVBounds3 rectBounds = inPathBuffer.m_PathRender->GetPathObjectStrokeBox(); + if (isParaboloidPass) { + rectBounds.scale(1.570796326795f); + } // PKC : More of the same ugly hack. + inShader.m_RectDimensions.Set(QT3DSVec4(rectBounds.minimum.x, rectBounds.minimum.y, + rectBounds.maximum.x, rectBounds.maximum.y)); + theRenderContext.SetInputAssembler(m_PaintedRectInputAssembler); + theRenderContext.SetCullingEnabled(false); + // Render exactly two triangles + theRenderContext.Draw(NVRenderDrawMode::Triangles, 6, 0); + theRenderContext.SetStencilTestEnabled(isStencilEnabled); + theRenderContext.SetDepthFunction(theDepthFunc); + } + + void RenderDepthPrepass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) override + { + SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path); + if (!thePathBuffer) { + return; + } + + if (thePathBuffer->m_PathType == PathTypes::Geometry) { + QT3DSU32 displacementIdx = 0; + QT3DSU32 imageIdx = 0; + SRenderableImage *displacementImage = 0; + + for (SRenderableImage *theImage = inRenderContext.m_FirstImage; + theImage != NULL && displacementImage == NULL; + theImage = theImage->m_NextImage, ++imageIdx) { + if (theImage->m_MapType == ImageMapTypes::Displacement) { + displacementIdx = imageIdx; + displacementImage = theImage; + } + } + + NVScopedRefCounted<SPathGeneratedShader> &theDesiredDepthShader = + displacementImage == NULL ? m_DepthShader : m_DepthDisplacementShader; + + if (!theDesiredDepthShader) { + IDefaultMaterialShaderGenerator &theMaterialGenerator( + m_RenderContext->GetDefaultMaterialShaderGenerator()); + SPathVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable(), false); + thePipeline.BeginVertexGeneration(displacementIdx, displacementImage); + thePipeline.BeginFragmentGeneration(); + thePipeline.Fragment().Append("\tfragOutput = vec4(1.0, 1.0, 1.0, 1.0);"); + thePipeline.EndVertexGeneration(false); + thePipeline.EndFragmentGeneration(false); + const char8_t *shaderName = "path depth"; + if (displacementImage) + shaderName = "path depth displacement"; + + SShaderCacheProgramFlags theFlags; + NVRenderShaderProgram *theProgram = + thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags, + inFeatureSet); + if (theProgram) { + theDesiredDepthShader = + QT3DS_NEW(m_RenderContext->GetAllocator(), + SPathGeneratedShader)(*theProgram, m_RenderContext->GetAllocator()); + } + } + if (theDesiredDepthShader) { + DoRenderGeometryPath(*theDesiredDepthShader, inRenderContext, inRenderProperties, + *thePathBuffer); + } + } else { + // painted path, go stroke route for now. + if (!m_PaintedDepthShader) { + IDefaultMaterialShaderGenerator &theMaterialGenerator( + m_RenderContext->GetDefaultMaterialShaderGenerator()); + SXYRectVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable()); + thePipeline.BeginVertexGeneration(0, NULL); + thePipeline.BeginFragmentGeneration(); + thePipeline.Fragment().Append("\tfragOutput = vec4(1.0, 1.0, 1.0, 1.0);"); + thePipeline.EndVertexGeneration(false); + thePipeline.EndFragmentGeneration(false); + const char8_t *shaderName = "path painted depth"; + SShaderCacheProgramFlags theFlags; + NVRenderShaderProgram *theProgram = + thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags, + inFeatureSet); + if (theProgram) { + m_PaintedDepthShader = + QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)( + *theProgram, m_RenderContext->GetAllocator()); + } + } + if (m_PaintedDepthShader) { + + DoRenderPaintedPath(*m_PaintedDepthShader, inRenderContext, inRenderProperties, + *thePathBuffer); + } + } + } + + void RenderShadowMapPass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) override + { + SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path); + if (!thePathBuffer) { + return; + } + + if (inRenderContext.m_Material.m_Type != GraphObjectTypes::DefaultMaterial) + return; + + if (thePathBuffer->m_PathType == PathTypes::Painted) { + // painted path, go stroke route for now. + if (!m_PaintedShadowShader) { + IDefaultMaterialShaderGenerator &theMaterialGenerator( + m_RenderContext->GetDefaultMaterialShaderGenerator()); + SXYRectVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable()); + thePipeline.OutputParaboloidDepthShaders(); + const char8_t *shaderName = "path painted paraboloid depth"; + SShaderCacheProgramFlags theFlags; + NVRenderShaderProgram *theProgram = + thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags, + inFeatureSet); + if (theProgram) { + m_PaintedShadowShader = + QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)( + *theProgram, m_RenderContext->GetAllocator()); + } + } + if (m_PaintedShadowShader) { + // Setup the shader paraboloid information. + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + theRenderContext.SetActiveShader(&m_PaintedShadowShader->m_Shader); + + DoRenderPaintedPath(*m_PaintedShadowShader, inRenderContext, inRenderProperties, + *thePathBuffer, true); + } + } else { + // Until we've also got a proper path render path for this, we'll call the old-fashioned + // stuff. + RenderDepthPrepass(inRenderContext, inRenderProperties, inFeatureSet); + // QT3DS_ASSERT( false ); + } + } + + void RenderCubeFaceShadowPass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) override + { + SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path); + if (!thePathBuffer) { + return; + } + + if (inRenderContext.m_Material.m_Type != GraphObjectTypes::DefaultMaterial) + return; + + if (thePathBuffer->m_PathType == PathTypes::Painted) { + if (!m_PaintedCubeShadowShader) { + IDefaultMaterialShaderGenerator &theMaterialGenerator( + m_RenderContext->GetDefaultMaterialShaderGenerator()); + SXYRectVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable()); + thePipeline.OutputCubeFaceDepthShaders(); + const char8_t *shaderName = "path painted cube face depth"; + SShaderCacheProgramFlags theFlags; + NVRenderShaderProgram *theProgram = + thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags, + inFeatureSet); + if (theProgram) { + m_PaintedCubeShadowShader = + QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)( + *theProgram, m_RenderContext->GetAllocator()); + } + } + if (m_PaintedCubeShadowShader) { + // Setup the shader information. + NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext()); + theRenderContext.SetActiveShader(&m_PaintedCubeShadowShader->m_Shader); + + m_PaintedCubeShadowShader->m_CameraPosition.Set( + inRenderContext.m_Camera.GetGlobalPos()); + m_PaintedCubeShadowShader->m_CameraProperties.Set( + QT3DSVec2(1.0f, inRenderContext.m_Camera.m_ClipFar)); + m_PaintedCubeShadowShader->m_ModelMatrix.Set(inRenderContext.m_ModelMatrix); + + DoRenderPaintedPath(*m_PaintedCubeShadowShader, inRenderContext, inRenderProperties, + *thePathBuffer, false); + } + } else { + // Until we've also got a proper path render path for this, we'll call the old-fashioned + // stuff. + RenderDepthPrepass(inRenderContext, inRenderProperties, inFeatureSet); + } + } + + void RenderPath(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) override + { + SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path); + if (!thePathBuffer) { + return; + } + + bool isDefaultMaterial = + (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial); + + if (thePathBuffer->m_PathType == PathTypes::Geometry) { + IMaterialShaderGenerator *theMaterialGenerator = + GetMaterialShaderGenertator(inRenderContext); + + // we need a more evolved key her for custom materials + // the same key can still need a different shader + SPathShaderMapKey sPathkey = SPathShaderMapKey(GetMaterialNameForKey(inRenderContext), + inRenderContext.m_MaterialKey); + eastl::pair<TShaderMap::iterator, bool> inserter = m_PathGeometryShaders.insert( + eastl::make_pair(sPathkey, NVScopedRefCounted<SPathGeneratedShader>(NULL))); + if (inserter.second) { + SPathVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), *theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable(), + m_RenderContext->GetWireframeMode()); + + NVRenderShaderProgram *theProgram = NULL; + + if (isDefaultMaterial) { + theProgram = theMaterialGenerator->GenerateShader( + inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline, + inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage, + inRenderContext.m_Opacity < 1.0, "path geometry pipeline-- "); + } else { + ICustomMaterialSystem &theMaterialSystem( + m_RenderContext->GetCustomMaterialSystem()); + const SCustomMaterial &theCustomMaterial( + reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material)); + + theProgram = theMaterialGenerator->GenerateShader( + inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline, + inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage, + inRenderContext.m_Opacity < 1.0, "path geometry pipeline-- ", + theMaterialSystem.GetShaderName(theCustomMaterial)); + } + + if (theProgram) + inserter.first->second = + QT3DS_NEW(m_RenderContext->GetAllocator(), + SPathGeneratedShader)(*theProgram, m_RenderContext->GetAllocator()); + } + if (!inserter.first->second) + return; + + DoRenderGeometryPath(*inserter.first->second.mPtr, inRenderContext, inRenderProperties, + *thePathBuffer); + } else { + IMaterialShaderGenerator *theMaterialGenerator = + GetMaterialShaderGenertator(inRenderContext); + + // we need a more evolved key her for custom materials + // the same key can still need a different shader + SPathShaderMapKey sPathkey = SPathShaderMapKey(GetMaterialNameForKey(inRenderContext), + inRenderContext.m_MaterialKey); + eastl::pair<TPaintedShaderMap::iterator, bool> inserter = m_PathPaintedShaders.insert( + eastl::make_pair(sPathkey, NVScopedRefCounted<SPathXYGeneratedShader>(NULL))); + + if (inserter.second) { + SXYRectVertexPipeline thePipeline( + m_RenderContext->GetShaderProgramGenerator(), *theMaterialGenerator, + m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable()); + + NVRenderShaderProgram *theProgram = NULL; + + if (isDefaultMaterial) { + theProgram = theMaterialGenerator->GenerateShader( + inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline, + inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage, + inRenderContext.m_Opacity < 1.0, "path painted pipeline-- "); + } else { + ICustomMaterialSystem &theMaterialSystem( + m_RenderContext->GetCustomMaterialSystem()); + const SCustomMaterial &theCustomMaterial( + reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material)); + + theProgram = theMaterialGenerator->GenerateShader( + inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline, + inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage, + inRenderContext.m_Opacity < 1.0, "path painted pipeline-- ", + theMaterialSystem.GetShaderName(theCustomMaterial)); + } + + if (theProgram) + inserter.first->second = + QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)( + *theProgram, m_RenderContext->GetAllocator()); + } + if (!inserter.first->second) + return; + + DoRenderPaintedPath(*inserter.first->second.mPtr, inRenderContext, inRenderProperties, + *thePathBuffer); + } + } +}; +} + +QT3DSVec2 IPathManagerCore::GetControlPointFromAngleDistance(QT3DSVec2 inPosition, float inIncomingAngle, + float inIncomingDistance) +{ + if (inIncomingDistance == 0.0f) + return inPosition; + float angleRad = degToRad(inIncomingAngle); + float angleSin = NVSin(angleRad); + float angleCos = NVCos(angleRad); + QT3DSVec2 relativeAngles = QT3DSVec2(angleCos * inIncomingDistance, angleSin * inIncomingDistance); + return inPosition + relativeAngles; +} + +QT3DSVec2 IPathManagerCore::GetAngleDistanceFromControlPoint(QT3DSVec2 inPosition, QT3DSVec2 inControlPoint) +{ + QT3DSVec2 relative = inControlPoint - inPosition; + float angleRad = atan2(relative.y, relative.x); + float distance = relative.magnitude(); + return QT3DSVec2(radToDeg(angleRad), distance); +} + +IPathManagerCore &IPathManagerCore::CreatePathManagerCore(IQt3DSRenderContextCore &ctx) +{ + return *QT3DS_NEW(ctx.GetAllocator(), SPathManager)(ctx); +} diff --git a/src/runtimerender/Qt3DSRenderPathManager.h b/src/runtimerender/Qt3DSRenderPathManager.h new file mode 100644 index 0000000..fc7e05f --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPathManager.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PATH_MANAGER_H +#define QT3DS_RENDER_PATH_MANAGER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderShaderCache.h" //TShaderFeatureSet +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSBounds3.h" +//#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" //SLayerGlobalRenderProperties + +namespace qt3ds { +namespace render { + + struct SLayerGlobalRenderProperties; + + struct SPathAnchorPoint + { + QT3DSVec2 m_Position; + QT3DSF32 m_IncomingAngle; + QT3DSF32 m_OutgoingAngle; + QT3DSF32 m_IncomingDistance; + QT3DSF32 m_OutgoingDistance; + SPathAnchorPoint() {} + SPathAnchorPoint(QT3DSVec2 inPos, QT3DSF32 inAngle, QT3DSF32 outAngle, QT3DSF32 inDis, QT3DSF32 outDis) + : m_Position(inPos) + , m_IncomingAngle(inAngle) + , m_OutgoingAngle(outAngle) + , m_IncomingDistance(inDis) + , m_OutgoingDistance(outDis) + { + } + }; + + class IPathManagerCore : public NVRefCounted + { + public: + // returns the path buffer id + //!! Note this call is made from multiple threads simultaneously during binary load. + //!! - see UICRenderGraphObjectSerializer.cpp + virtual void + SetPathSubPathData(const SPathSubPath &inPathSubPath, + NVConstDataRef<SPathAnchorPoint> inPathSubPathAnchorPoints) = 0; + + virtual NVDataRef<SPathAnchorPoint> + GetPathSubPathBuffer(const SPathSubPath &inPathSubPath) = 0; + // Marks the PathSubPath anchor points as dirty. This will mean rebuilding any PathSubPath + // context required to render the PathSubPath. + virtual NVDataRef<SPathAnchorPoint> + ResizePathSubPathBuffer(const SPathSubPath &inPathSubPath, QT3DSU32 inNumAnchors) = 0; + virtual NVBounds3 GetBounds(const SPath &inPath) = 0; + + // Helper functions used in various locations + // Angles here are in degrees because that is how they are represented in the data. + static QT3DSVec2 GetControlPointFromAngleDistance(QT3DSVec2 inPosition, float inAngle, + float inDistance); + + // Returns angle in x, distance in y. + static QT3DSVec2 GetAngleDistanceFromControlPoint(QT3DSVec2 inPosition, QT3DSVec2 inControlPoint); + + virtual IPathManager &OnRenderSystemInitialize(IQt3DSRenderContext &context) = 0; + + static IPathManagerCore &CreatePathManagerCore(IQt3DSRenderContextCore &inContext); + }; + + struct SPathRenderContext; // UICRenderPathRenderContext.h + + class IPathManager : public IPathManagerCore + { + public: + // The path segments are next expected to change after this call; changes will be ignored. + virtual bool PrepareForRender(const SPath &inPath) = 0; + + virtual void RenderDepthPrepass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) = 0; + + virtual void RenderShadowMapPass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) = 0; + + virtual void RenderCubeFaceShadowPass(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) = 0; + + virtual void RenderPath(SPathRenderContext &inRenderContext, + SLayerGlobalRenderProperties inRenderProperties, + TShaderFeatureSet inFeatureSet) = 0; + }; +} +} +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderPathMath.h b/src/runtimerender/Qt3DSRenderPathMath.h new file mode 100644 index 0000000..e9eb222 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPathMath.h @@ -0,0 +1,713 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 QT3DS_RENDER_PATH_MATH_H +#define QT3DS_RENDER_PATH_MATH_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSVec3.h" +namespace qt3ds { +namespace render { +namespace path { +// Solve quadratic equation in with a templated real number system. +template <typename REAL> + +int quadratic(REAL b, REAL c, REAL rts[2]) +{ + int nquad; + REAL dis; + REAL rtdis; + + dis = b * b - 4 * c; + rts[0] = 0; + rts[1] = 0; + if (b == 0) { + if (c == 0) { + nquad = 2; + } else { + if (c < 0) { + nquad = 2; + rts[0] = sqrt(-c); + rts[1] = -rts[0]; + } else { + nquad = 0; + } + } + } else if (c == 0) { + nquad = 2; + rts[0] = -b; + } else if (dis >= 0) { + nquad = 2; + rtdis = sqrt(dis); + if (b > 0) + rts[0] = (-b - rtdis) * (1 / REAL(2)); + else + rts[0] = (-b + rtdis) * (1 / REAL(2)); + if (rts[0] == 0) + rts[1] = -b; + else + rts[1] = c / rts[0]; + } else { + nquad = 0; + } + + return (nquad); +} /* quadratic */ + +float interest_range[2] = {0, 1}; + +void cubicInflectionPoint(const QT3DSVec2 cp[4], nvvector<QT3DSF32> &key_point) +{ + // Convert control points to cubic monomial polynomial coefficients + const QT3DSVec2 A = cp[3] - cp[0] + (cp[1] - cp[2]) * 3.0; + const QT3DSVec2 B = (cp[0] - cp[1] * 2.0 + cp[2]) * 3.0, C = (cp[1] - cp[0]) * 3.0; + const QT3DSVec2 D = cp[0]; + + double a = 3 * (B.x * A.y - A.x * B.y); + double b = 3 * (C.x * A.y - C.y * A.x); + double c = C.x * B.y - C.y * B.x; + + double roots[2]; + int solutions; + // Is the quadratic really a degenerate line? + if (a == 0) { + // Is the line really a degenerate point? + if (b == 0) { + solutions = 0; + } else { + solutions = 1; + roots[0] = c / b; + } + } else { + solutions = quadratic(b / a, c / a, roots); + } + for (int i = 0; i < solutions; i++) { + QT3DSF32 t = static_cast<QT3DSF32>(roots[i]); + + QT3DSVec2 p = ((A * t + B) * t + C) * t + D; + if (t >= interest_range[0] && t <= interest_range[1]) + key_point.push_back(t); + // else; Outside range of interest, ignore. + } +} + +typedef enum { + CT_POINT, + CT_LINE, + CT_QUADRATIC, + CT_CUSP, + CT_LOOP, + CT_SERPENTINE +} CurveType; + +static inline bool isZero(double v) +{ +#if 0 + const double eps = 6e-008; + + if (fabs(v) < eps) + return true; + else + return false; +#else + return v == 0.0; +#endif +} + +inline QT3DSVec3 crossv1(const QT3DSVec2 &a, const QT3DSVec2 &b) +{ + return QT3DSVec3(a[1] - b[1], b[0] - a[0], a[0] * b[1] - a[1] * b[0]); +} + +inline bool sameVertex(const QT3DSVec2 &a, const QT3DSVec2 &b) +{ + return (a.x == b.x && a.y == b.y); +} + +inline bool sameVertex(const QT3DSVec3 &a, const QT3DSVec3 &b) +{ + return (a.x == b.x && a.y == b.y && a.z == b.z); +} + +// This function "normalizes" the input vector so the larger of its components +// is in the range [512,1024]. Exploit integer math on the exponent bits to +// do this without expensive DP exponentiation. +inline void scaleTo512To1024(QT3DSVec2 &d, int e) +{ + union { + QT3DSU64 u64; + double f64; + } x; + int ie = 10 - (int)e + 1023; + QT3DS_ASSERT(ie > 0); + x.u64 = ((QT3DSU64)ie) << 52; + d *= static_cast<QT3DSF32>(x.f64); +} + +inline double fastfrexp(double d, int *exponent) +{ + union { + QT3DSU64 u64; + double f64; + } x; + x.f64 = d; + *exponent = (((int)(x.u64 >> 52)) & 0x7ff) - 0x3ff; + x.u64 &= (1ULL << 63) - (1ULL << 52); + x.u64 |= (0x3ffULL << 52); + return x.f64; +} + +QT3DSVec3 CreateVec3(QT3DSVec2 xy, float z) +{ + return QT3DSVec3(xy.x, xy.y, z); +} + +QT3DSVec2 GetXY(const QT3DSVec3 &data) +{ + return QT3DSVec2(data.x, data.y); +} + +CurveType cubicDoublePoint(const QT3DSVec2 points[4], nvvector<QT3DSF32> &key_point) +{ +#if 0 + const QT3DSVec2 AA = points[3] - points[0] + (points[1] - points[2]) * 3.0; + const QT3DSVec3 BB = (points[0] - points[1] * 2.0 + points[2]) * 3.0; + const QT3DSVec3 CC = (points[1] - points[0]) * 3.0, DD = points[0]; +#endif + + // Assume control points of the cubic curve are A, B, C, and D. + const QT3DSVec3 A = CreateVec3(points[0], 1); + const QT3DSVec3 B = CreateVec3(points[1], 1); + const QT3DSVec3 C = CreateVec3(points[2], 1); + const QT3DSVec3 D = CreateVec3(points[3], 1); + + // Compute the discriminant of the roots of + // H(s,t) = -36*(d1^2*s^2 - d1*d2*s*t + (d2^2 - d1*d3)*t^2) + // where H is the Hessian (the square matrix of second-order + // partial derivatives of a function) of I(s,t) + // where I(s,t) determine the inflection points of the cubic + // Bezier curve C(s,t). + // + // d1, d2, and d3 functions of the determinants constructed + // from the cubic control points. + // + // Recall dot(a,cross(b,c)) is determinant of a 3x3 matrix + // with a, b, c the rows of the matrix. + const QT3DSVec3 DC = crossv1(GetXY(D), GetXY(C)); + const QT3DSVec3 AD = crossv1(GetXY(A), GetXY(D)); + const QT3DSVec3 BA = crossv1(GetXY(B), GetXY(A)); + + const double a1 = A.dot(DC); + const double a2 = B.dot(AD); + const double a3 = C.dot(BA); + const double d1 = a1 - 2 * a2 + 3 * a3; + const double d2 = -a2 + 3 * a3; + const double d3 = 3 * a3; + const double discriminant = (3 * d2 * d2 - 4 * d1 * d3); + + // The sign of the discriminant of I classifies the curbic curve + // C into one of 6 classifications: + // 1) discriminant>0 ==> serpentine + // 2) discriminant=0 ==> cusp + // 3) discriminant<0 ==> loop + + // If the discriminant or d1 are almost but not exactly zero, the + // result is really noisy unacceptable (k,l,m) interpolation. + // If it looks almost like a quadratic or linear case, treat it that way. + if (isZero(discriminant) && isZero(d1)) { + // Cusp case + + if (isZero(d2)) { + // degenerate cases (points, lines, quadratics)... + if (isZero(d3)) { + if (sameVertex(A, B) && sameVertex(A, C) && sameVertex(A, D)) + return CT_POINT; + else + return CT_LINE; + } else { + return CT_QUADRATIC; + } + } else { + return CT_CUSP; + } + } else if (discriminant < 0) { + // Loop case + + const QT3DSF32 t = static_cast<QT3DSF32>(d2 + sqrt(-discriminant)); + QT3DSVec2 d = QT3DSVec2(t, static_cast<QT3DSF32>(2 * d1)); + QT3DSVec2 e = QT3DSVec2(static_cast<QT3DSF32>(2 * (d2 * d2 - d1 * d3)), + static_cast<QT3DSF32>(d1 * t)); + + // There is the situation where r2=c/t results in division by zero, but + // in this case, the two roots represent a double root at zero so + // subsitute l for (the otherwise NaN) m in this case. + // + // This situation can occur when the 1st and 2nd (or 3rd and 4th?) + // control point of a cubic Bezier path SubPath are identical. + if (e.x == 0 && e.y == 0) + e = d; + + // d, e, or both could be very large values. To mitigate the risk of + // floating-point overflow in subsequent calculations + // scale both vectors to be in the range [768,1024] since their relative + // scale of their x & y components is irrelevant. + + // Be careful to divide by a power-of-two to disturb mantissa bits. + + double d_max_mag = NVMax(fabs(d.x), fabs(d.y)); + int exponent; + fastfrexp(d_max_mag, &exponent); + scaleTo512To1024(d, exponent); + + double e_max_mag = NVMax(fabs(e.x), fabs(e.y)); + fastfrexp(e_max_mag, &exponent); + scaleTo512To1024(e, exponent); + + const QT3DSVec2 roots = QT3DSVec2(d.x / d.y, e.x / e.y); + + double tt; +#if 0 + tt = roots[0]; + if (tt >= interest_range[0] && tt <= interest_range[1]) + // key_point.push_back(tt); + tt = roots[1]; + if (tt >= interest_range[0] && tt <= interest_range[1]) + // key_point.push_back(tt); +#endif + tt = (roots[0] + roots[1]) / 2; + if (tt >= interest_range[0] && tt <= interest_range[1]) + key_point.push_back(static_cast<QT3DSF32>(tt)); + + return CT_LOOP; + } else { + QT3DS_ASSERT(discriminant >= 0); + cubicInflectionPoint(points, key_point); + if (discriminant > 0) { + // Serpentine case + return CT_SERPENTINE; + } else { + // Cusp with inflection at infinity (treat like serpentine) + return CT_CUSP; + } + } +} + +QT3DSVec4 CreateVec4(QT3DSVec2 p1, QT3DSVec2 p2) +{ + return QT3DSVec4(p1.x, p1.y, p2.x, p2.y); +} + +QT3DSVec2 lerp(QT3DSVec2 p1, QT3DSVec2 p2, QT3DSF32 distance) +{ + return p1 + (p2 - p1) * distance; +} + +QT3DSF32 lerp(QT3DSF32 p1, QT3DSF32 p2, QT3DSF32 distance) +{ + return p1 + (p2 - p1) * distance; +} + +// Using first derivative to get tangent. +// If this equation does not make immediate sense consider that it is the first derivative +// of the de Casteljau bezier expansion, not the polynomial expansion. +float TangentAt(float inT, float p1, float c1, float c2, float p2) +{ + float a = c1 - p1; + float b = c2 - c1 - a; + float c = p2 - c2 - a - (2.0f * b); + float retval = 3.0f * (a + (2.0f * b * inT) + (c * inT * inT)); + return retval; +} + +QT3DSVec2 midpoint(QT3DSVec2 p1, QT3DSVec2 p2) +{ + return lerp(p1, p2, .5f); +} + +QT3DSF32 LineLength(QT3DSVec2 inStart, QT3DSVec2 inStop) +{ + return (inStop - inStart).magnitude(); +} + +struct SCubicBezierCurve +{ + QT3DSVec2 m_Points[4]; + SCubicBezierCurve(QT3DSVec2 a1, QT3DSVec2 c1, QT3DSVec2 c2, QT3DSVec2 a2) + { + m_Points[0] = a1; + m_Points[1] = c1; + m_Points[2] = c2; + m_Points[3] = a2; + } + + // Normal is of course orthogonal to the tangent. + QT3DSVec2 NormalAt(float inT) const + { + QT3DSVec2 tangent = QT3DSVec2( + TangentAt(inT, m_Points[0].x, m_Points[1].x, m_Points[2].x, m_Points[3].x), + TangentAt(inT, m_Points[0].y, m_Points[1].y, m_Points[2].y, m_Points[3].y)); + + QT3DSVec2 result(tangent.y, -tangent.x); + result.normalize(); + return result; + } + + eastl::pair<SCubicBezierCurve, SCubicBezierCurve> SplitCubicBezierCurve(float inT) + { + // compute point on curve based on inT + // using de Casteljau algorithm + QT3DSVec2 p12 = lerp(m_Points[0], m_Points[1], inT); + QT3DSVec2 p23 = lerp(m_Points[1], m_Points[2], inT); + QT3DSVec2 p34 = lerp(m_Points[2], m_Points[3], inT); + QT3DSVec2 p123 = lerp(p12, p23, inT); + QT3DSVec2 p234 = lerp(p23, p34, inT); + QT3DSVec2 p1234 = lerp(p123, p234, inT); + + return eastl::make_pair(SCubicBezierCurve(m_Points[0], p12, p123, p1234), + SCubicBezierCurve(p1234, p234, p34, m_Points[3])); + } +}; + +#if 0 + static QT3DSVec2 NormalToLine( QT3DSVec2 startPoint, QT3DSVec2 endPoint ) + { + QT3DSVec2 lineDxDy = endPoint - startPoint; + QT3DSVec2 result( lineDxDy.y, -lineDxDy.x ); + result.normalize(); + return result; + } +#endif + +struct SResultCubic +{ + enum Mode { + Normal = 0, + BeginTaper = 1, + EndTaper = 2, + }; + QT3DSVec2 m_P1; + QT3DSVec2 m_C1; + QT3DSVec2 m_C2; + QT3DSVec2 m_P2; + // Location in the original data where this cubic is taken from + QT3DSU32 m_EquationIndex; + QT3DSF32 m_TStart; + QT3DSF32 m_TStop; + QT3DSF32 m_Length; + QT3DSVec2 m_TaperMultiplier; // normally 1, goes to zero at very end of taper if any taper. + Mode m_Mode; + + SResultCubic(QT3DSVec2 inP1, QT3DSVec2 inC1, QT3DSVec2 inC2, QT3DSVec2 inP2, + QT3DSU32 equationIndex, QT3DSF32 tStart, QT3DSF32 tStop, QT3DSF32 length) + : m_P1(inP1) + , m_C1(inC1) + , m_C2(inC2) + , m_P2(inP2) + , m_EquationIndex(equationIndex) + , m_TStart(tStart) + , m_TStop(tStop) + , m_Length(length) + , m_TaperMultiplier(1.0f, 1.0f) + , m_Mode(Normal) + { + } + // Note the vec2 items are *not* initialized in any way here. + SResultCubic() {} + QT3DSF32 GetP1Width(QT3DSF32 inPathWidth, QT3DSF32 beginTaperWidth, QT3DSF32 endTaperWidth) + { + return GetPathWidth(inPathWidth, beginTaperWidth, endTaperWidth, 0); + } + + QT3DSF32 GetP2Width(QT3DSF32 inPathWidth, QT3DSF32 beginTaperWidth, QT3DSF32 endTaperWidth) + { + return GetPathWidth(inPathWidth, beginTaperWidth, endTaperWidth, 1); + } + + QT3DSF32 GetPathWidth(QT3DSF32 inPathWidth, QT3DSF32 beginTaperWidth, QT3DSF32 endTaperWidth, + QT3DSU32 inTaperIndex) + { + QT3DSF32 retval = inPathWidth; + switch (m_Mode) { + case BeginTaper: + retval = beginTaperWidth * m_TaperMultiplier[inTaperIndex]; + break; + case EndTaper: + retval = endTaperWidth * m_TaperMultiplier[inTaperIndex]; + break; + default: + break; + } + return retval; + } +}; + +void PushLine(nvvector<SResultCubic> &ioResultVec, QT3DSVec2 inStart, QT3DSVec2 inStop, + QT3DSU32 inEquationIndex) +{ + QT3DSVec2 range = inStop - inStart; + ioResultVec.push_back(SResultCubic(inStart, inStart + range * .333f, + inStart + range * .666f, inStop, inEquationIndex, + 0.0f, 1.0f, LineLength(inStart, inStop))); +} + +struct PathDirtyFlagValues +{ + enum Enum { + SourceData = 1, + PathType = 1 << 1, + Width = 1 << 2, + BeginTaper = 1 << 3, + EndTaper = 1 << 4, + CPUError = 1 << 5, + }; +}; + +struct SPathDirtyFlags : public NVFlags<PathDirtyFlagValues::Enum> +{ + typedef NVFlags<PathDirtyFlagValues::Enum> TBase; + SPathDirtyFlags() {} + SPathDirtyFlags(int inFlags) + : TBase(static_cast<PathDirtyFlagValues::Enum>(inFlags)) + { + } + void Clear() + { + *this = SPathDirtyFlags(); + } +}; + +struct STaperInformation +{ + QT3DSF32 m_CapOffset; + QT3DSF32 m_CapOpacity; + QT3DSF32 m_CapWidth; + + STaperInformation() + : m_CapOffset(0) + , m_CapOpacity(0) + , m_CapWidth(0) + { + } + STaperInformation(QT3DSF32 capOffset, QT3DSF32 capOpacity, QT3DSF32 capWidth) + : m_CapOffset(capOffset) + , m_CapOpacity(capOpacity) + , m_CapWidth(capWidth) + { + } + + bool operator==(const STaperInformation &inOther) const + { + return m_CapOffset == inOther.m_CapOffset && m_CapOpacity == inOther.m_CapOpacity + && m_CapWidth == inOther.m_CapWidth; + } +}; + +template <typename TOptData> +bool OptionEquals(const Option<TOptData> &lhs, const Option<TOptData> &rhs) +{ + if (lhs.hasValue() != rhs.hasValue()) + return false; + if (lhs.hasValue()) + return lhs.getValue() == rhs.getValue(); + return true; +} +void OuterAdaptiveSubdivideBezierCurve(nvvector<SResultCubic> &ioResultVec, + nvvector<QT3DSF32> &keyPointVec, + SCubicBezierCurve inCurve, QT3DSF32 inLinearError, + QT3DSU32 inEquationIndex); + +void AdaptiveSubdivideBezierCurve(nvvector<SResultCubic> &ioResultVec, + SCubicBezierCurve &inCurve, QT3DSF32 inLinearError, + QT3DSU32 inEquationIndex, QT3DSF32 inTStart, QT3DSF32 inTStop); + +// Adaptively subdivide source data to produce m_PatchData. +void AdaptiveSubdivideSourceData(NVConstDataRef<SPathAnchorPoint> inSourceData, + nvvector<SResultCubic> &ioResultVec, + nvvector<QT3DSF32> &keyPointVec, QT3DSF32 inLinearError) +{ + ioResultVec.clear(); + if (inSourceData.size() < 2) + return; + // Assuming no attributes in the source data. + QT3DSU32 numEquations = (inSourceData.size() - 1); + for (QT3DSU32 idx = 0, end = numEquations; idx < end; ++idx) { + const SPathAnchorPoint &beginAnchor = inSourceData[idx]; + const SPathAnchorPoint &endAnchor = inSourceData[idx + 1]; + + QT3DSVec2 anchor1(beginAnchor.m_Position); + QT3DSVec2 control1(IPathManagerCore::GetControlPointFromAngleDistance( + beginAnchor.m_Position, beginAnchor.m_OutgoingAngle, + beginAnchor.m_OutgoingDistance)); + + QT3DSVec2 control2(IPathManagerCore::GetControlPointFromAngleDistance( + endAnchor.m_Position, endAnchor.m_IncomingAngle, + endAnchor.m_IncomingDistance)); + QT3DSVec2 anchor2(endAnchor.m_Position); + + OuterAdaptiveSubdivideBezierCurve( + ioResultVec, keyPointVec, + SCubicBezierCurve(anchor1, control1, control2, anchor2), inLinearError, idx); + } +} + +// The outer subdivide function topologically analyzes the curve to ensure that +// the sign of the second derivative does not change, no inflection points. +// Once that condition is held, then we proceed with a simple adaptive subdivision algorithm +// until the curve is accurately approximated by a straight line. +void OuterAdaptiveSubdivideBezierCurve(nvvector<SResultCubic> &ioResultVec, + nvvector<QT3DSF32> &keyPointVec, + SCubicBezierCurve inCurve, QT3DSF32 inLinearError, + QT3DSU32 inEquationIndex) +{ + // Step 1, find what type of curve we are dealing with and the inflection points. + keyPointVec.clear(); + CurveType theCurveType = cubicDoublePoint(inCurve.m_Points, keyPointVec); + + QT3DSF32 tStart = 0; + switch (theCurveType) { + case CT_POINT: + ioResultVec.push_back(SResultCubic(inCurve.m_Points[0], inCurve.m_Points[0], + inCurve.m_Points[0], inCurve.m_Points[0], + inEquationIndex, 0.0f, 1.0f, 0.0f)); + return; // don't allow further recursion + case CT_LINE: + PushLine(ioResultVec, inCurve.m_Points[0], inCurve.m_Points[3], inEquationIndex); + return; // don't allow further recursion + case CT_CUSP: + case CT_LOOP: + case CT_SERPENTINE: { + // Break the curve at the inflection points if there is one. If there aren't + // inflection points + // the treat as linear (degenerate case that should not happen except in limiting + // ranges of floating point accuracy) + if (!keyPointVec.empty()) { + // It is not clear that the code results in a sorted vector, + // or a vector where all values are within the range of 0-1 + if (keyPointVec.size() > 1) + eastl::sort(keyPointVec.begin(), keyPointVec.end()); + for (QT3DSU32 idx = 0, end = (QT3DSU32)keyPointVec.size(); + idx < end && keyPointVec[idx] < 1.0f; ++idx) { + // We have a list of T values I believe sorted from beginning to end, we + // will create a set of bezier curves + // Since we split the curves, tValue is relative to tSTart, not 0. + QT3DSF32 range = 1.0f - tStart; + QT3DSF32 splitPoint = keyPointVec[idx] - tStart; + QT3DSF32 tValue = splitPoint / range; + if (tValue > 0.0f) { + eastl::pair<SCubicBezierCurve, SCubicBezierCurve> newCurves + = inCurve.SplitCubicBezierCurve(tValue); + AdaptiveSubdivideBezierCurve(ioResultVec, newCurves.first, + inLinearError, inEquationIndex, tStart, + splitPoint); + inCurve = newCurves.second; + tStart = splitPoint; + } + } + } + } + // fallthrough intentional + break; + // fallthrough intentional + case CT_QUADRATIC: + break; + } + AdaptiveSubdivideBezierCurve(ioResultVec, inCurve, inLinearError, inEquationIndex, + tStart, 1.0f); +} + +static QT3DSF32 DistanceFromPointToLine(QT3DSVec2 inLineDxDy, QT3DSVec2 lineStart, QT3DSVec2 point) +{ + QT3DSVec2 pointToLineStart = lineStart - point; + return fabs((inLineDxDy.x * pointToLineStart.y) - (inLineDxDy.y * pointToLineStart.x)); +} + +// There are two options here. The first is to just subdivide below a given error +// tolerance. +// The second is to fit a quadratic to the curve and then precisely find the length of the +// quadratic. +// Obviously we are choosing the subdivide method at this moment but I think the fitting +// method is probably more robust. +QT3DSF32 LengthOfBezierCurve(SCubicBezierCurve &inCurve) +{ + // Find distance of control points from line. Note that both control points should be + // on same side of line else we have a serpentine which should have been removed by topological + // analysis. + QT3DSVec2 lineDxDy = inCurve.m_Points[3] - inCurve.m_Points[0]; + QT3DSF32 c1Distance = DistanceFromPointToLine( + lineDxDy, inCurve.m_Points[0], inCurve.m_Points[1]); + QT3DSF32 c2Distance = DistanceFromPointToLine( + lineDxDy, inCurve.m_Points[0], inCurve.m_Points[2]); + const float lineTolerance = 100.0f; // error in world coordinates, squared. + if (c1Distance > lineTolerance || c2Distance > lineTolerance) { + eastl::pair<SCubicBezierCurve, SCubicBezierCurve> subdivCurve + = inCurve.SplitCubicBezierCurve(.5f); + return LengthOfBezierCurve(subdivCurve.first) + + LengthOfBezierCurve(subdivCurve.second); + } else { + return LineLength(inCurve.m_Points[0], inCurve.m_Points[3]); + } +} + +// The assumption here is the the curve type is not cusp, loop, or serpentine. +// It is either linear or it is a constant curve meaning we can use very simple means to +// figure out the curvature. There is a possibility to use some math to figure out the point of +// maximum curvature, where the second derivative will have a max value. This is probably not +// necessary. +void AdaptiveSubdivideBezierCurve(nvvector<SResultCubic> &ioResultVec, + SCubicBezierCurve &inCurve, QT3DSF32 inLinearError, + QT3DSU32 inEquationIndex, QT3DSF32 inTStart, QT3DSF32 inTStop) +{ + // Find distance of control points from line. Note that both control points should be + // on same side of line else we have a serpentine which should have been removed by topological + // analysis. + QT3DSVec2 lineDxDy = inCurve.m_Points[3] - inCurve.m_Points[0]; + QT3DSF32 c1Distance = DistanceFromPointToLine(lineDxDy, inCurve.m_Points[0], + inCurve.m_Points[1]); + QT3DSF32 c2Distance = DistanceFromPointToLine(lineDxDy, inCurve.m_Points[0], + inCurve.m_Points[2]); + const float lineTolerance = inLinearError * inLinearError; // error in world coordinates + if (c1Distance > lineTolerance || c2Distance > lineTolerance) { + eastl::pair<SCubicBezierCurve, SCubicBezierCurve> subdivCurve + = inCurve.SplitCubicBezierCurve(.5f); + QT3DSF32 halfway = lerp(inTStart, inTStop, .5f); + AdaptiveSubdivideBezierCurve(ioResultVec, subdivCurve.first, inLinearError, + inEquationIndex, inTStart, halfway); + AdaptiveSubdivideBezierCurve(ioResultVec, subdivCurve.second, inLinearError, + inEquationIndex, halfway, inTStop); + } else { + ioResultVec.push_back(SResultCubic(inCurve.m_Points[0], inCurve.m_Points[1], + inCurve.m_Points[2], inCurve.m_Points[3], + inEquationIndex, inTStart, inTStop, + LengthOfBezierCurve(inCurve))); + } +} +} +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderPathRenderContext.h b/src/runtimerender/Qt3DSRenderPathRenderContext.h new file mode 100644 index 0000000..deeea28 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPathRenderContext.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PATH_RENDER_CONTEXT_H +#define QT3DS_RENDER_PATH_RENDER_CONTEXT_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderShaderCache.h" //TShaderFeatureSet +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSBounds3.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderableImage.h" + +namespace qt3ds { +namespace render { + + struct SPathRenderContext + { + // The lights and camera will not change per layer, + // so that information can be set once for all the shaders. + NVConstDataRef<SLight *> m_Lights; + const SCamera &m_Camera; + + // Per-object information. + const SPath &m_Path; + const QT3DSMat44 &m_ModelViewProjection; + const QT3DSMat44 &m_ModelMatrix; ///< model to world transformation + const QT3DSMat33 &m_NormalMatrix; + + QT3DSF32 m_Opacity; + const SGraphObject &m_Material; + SShaderDefaultMaterialKey m_MaterialKey; + SRenderableImage *m_FirstImage; + QT3DSVec2 m_CameraVec; + + bool m_EnableWireframe; + bool m_HasTransparency; + bool m_IsStroke; + + SPathRenderContext(NVConstDataRef<SLight *> lights, const SCamera &cam, const SPath &p, + const QT3DSMat44 &mvp, const QT3DSMat44 &world, const QT3DSMat33 &nm, + QT3DSF32 inOpacity, const SGraphObject &inMaterial, + SShaderDefaultMaterialKey inMaterialKey, SRenderableImage *inFirstImage, + bool inWireframe, QT3DSVec2 inCameraVec, bool inHasTransparency, + bool inIsStroke) + + : m_Lights(lights) + , m_Camera(cam) + , m_Path(p) + , m_ModelViewProjection(mvp) + , m_ModelMatrix(world) + , m_NormalMatrix(nm) + , m_Opacity(inOpacity) + , m_Material(inMaterial) + , m_MaterialKey(inMaterialKey) + , m_FirstImage(inFirstImage) + , m_CameraVec(inCameraVec) + , m_EnableWireframe(inWireframe) + , m_HasTransparency(inHasTransparency) + , m_IsStroke(inIsStroke) + { + } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.cpp b/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.cpp new file mode 100644 index 0000000..43f7c0b --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPixelGraphicsRenderer.h" +#include "Qt3DSRenderPixelGraphicsTypes.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderShaderCache.h" + +using namespace qt3ds; +using namespace qt3ds::render; + +namespace { + +struct SPGRectShader +{ + NVScopedRefCounted<NVRenderShaderProgram> m_RectShader; + NVRenderShaderConstantBase *mvp; + NVRenderShaderConstantBase *rectColor; + NVRenderShaderConstantBase *leftright; + NVRenderShaderConstantBase *bottomtop; + + SPGRectShader() + : mvp(NULL) + , rectColor(NULL) + , leftright(NULL) + , bottomtop(NULL) + { + } + void SetShader(NVRenderShaderProgram *program) + { + m_RectShader = program; + if (program) { + mvp = program->GetShaderConstant("model_view_projection"); + rectColor = program->GetShaderConstant("rect_color"); + leftright = program->GetShaderConstant("leftright[0]"); + bottomtop = program->GetShaderConstant("bottomtop[0]"); + } + } + + void Apply(QT3DSMat44 &inVP, const SPGRect &inObject) + { + if (mvp) + m_RectShader->SetConstantValue(mvp, toConstDataRef(inVP), 1); + if (rectColor) + m_RectShader->SetConstantValue(rectColor, inObject.m_FillColor, 1); + if (leftright) { + QT3DSF32 theData[] = { inObject.m_Left, inObject.m_Right }; + m_RectShader->SetConstantValue(leftright, *theData, 2); + } + if (bottomtop) { + QT3DSF32 theData[] = { inObject.m_Bottom, inObject.m_Top }; + m_RectShader->SetConstantValue(bottomtop, *theData, 2); + } + } + + operator bool() { return m_RectShader.mPtr != NULL; } +}; + +struct SPGRenderer : public IPixelGraphicsRenderer +{ + IQt3DSRenderContext &m_RenderContext; + IStringTable &m_StringTable; + NVScopedRefCounted<NVRenderVertexBuffer> m_QuadVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_QuadIndexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_QuadInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_QuadAttribLayout; + SShaderVertexCodeGenerator m_VertexGenerator; + SShaderFragmentCodeGenerator m_FragmentGenerator; + SPGRectShader m_RectShader; + QT3DSI32 mRefCount; + + SPGRenderer(IQt3DSRenderContext &ctx, IStringTable &strt) + : m_RenderContext(ctx) + , m_StringTable(strt) + , m_VertexGenerator(m_StringTable, ctx.GetAllocator(), + m_RenderContext.GetRenderContext().GetRenderContextType()) + , m_FragmentGenerator(m_VertexGenerator, ctx.GetAllocator(), + m_RenderContext.GetRenderContext().GetRenderContextType()) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) + void GetRectShaderProgram() + { + if (!m_RectShader) { + m_VertexGenerator.Begin(); + m_FragmentGenerator.Begin(); + m_VertexGenerator.AddAttribute("attr_pos", "vec2"); + m_VertexGenerator.AddUniform("model_view_projection", "mat4"); + m_VertexGenerator.AddUniform("leftright[2]", "float"); + m_VertexGenerator.AddUniform("bottomtop[2]", "float"); + m_FragmentGenerator.AddVarying("rect_uvs", "vec2"); + m_FragmentGenerator.AddUniform("rect_color", "vec4"); + m_VertexGenerator << "void main() {" << Endl + << "\tgl_Position = model_view_projection * vec4( " + "leftright[int(attr_pos.x)], bottomtop[int(attr_pos.y)], 0.0, 1.0 " + ");" + << Endl << "\trect_uvs = attr_pos;" << Endl << "}" << Endl; + + m_FragmentGenerator << "void main() {" << Endl << "\tfragOutput = rect_color;" << Endl + << "}" << Endl; + + m_VertexGenerator.BuildShaderSource(); + m_FragmentGenerator.BuildShaderSource(); + + m_RectShader.SetShader(m_RenderContext.GetShaderCache().CompileProgram( + m_StringTable.RegisterStr("PixelRectShader"), + m_VertexGenerator.m_FinalShaderBuilder.c_str(), + m_FragmentGenerator.m_FinalShaderBuilder.c_str(), NULL // no tess control shader + , + NULL // no tess eval shader + , + NULL // no geometry shader + , + SShaderCacheProgramFlags(), ShaderCacheNoFeatures())); + } + } + void GenerateXYQuad() + { + NVRenderContext &theRenderContext(m_RenderContext.GetRenderContext()); + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2), + }; + + QT3DSVec2 pos[] = { QT3DSVec2(0, 0), QT3DSVec2(0, 1), QT3DSVec2(1, 1), QT3DSVec2(1, 0) }; + + if (m_QuadVertexBuffer == NULL) { + size_t bufSize = sizeof(pos); + m_QuadVertexBuffer = theRenderContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, bufSize, 2 * sizeof(QT3DSF32), + toU8DataRef(pos, 4)); + } + + if (m_QuadIndexBuffer == NULL) { + QT3DSU8 indexData[] = { + 0, 1, 2, 0, 2, 3, + }; + m_QuadIndexBuffer = theRenderContext.CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, + qt3ds::render::NVRenderComponentTypes::QT3DSU8, sizeof(indexData), + toU8DataRef(indexData, sizeof(indexData))); + } + + if (m_QuadAttribLayout == NULL) { + // create our attribute layout + m_QuadAttribLayout = + theRenderContext.CreateAttributeLayout(toConstDataRef(theEntries, 1)); + } + + if (m_QuadInputAssembler == NULL) { + + // create input assembler object + QT3DSU32 strides = m_QuadVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_QuadInputAssembler = theRenderContext.CreateInputAssembler( + m_QuadAttribLayout, toConstDataRef(&m_QuadVertexBuffer.mPtr, 1), m_QuadIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + } + + void RenderPixelObject(QT3DSMat44 &inProjection, const SPGRect &inObject) + { + GenerateXYQuad(); + GetRectShaderProgram(); + if (m_RectShader) { + m_RenderContext.GetRenderContext().SetActiveShader(m_RectShader.m_RectShader.mPtr); + m_RectShader.Apply(inProjection, inObject); + + m_RenderContext.GetRenderContext().SetInputAssembler(m_QuadInputAssembler.mPtr); + m_RenderContext.GetRenderContext().Draw(NVRenderDrawMode::Triangles, + m_QuadInputAssembler->GetIndexCount(), 0); + } + } + + void RenderPixelObject(QT3DSMat44 &inProjection, const SPGVertLine &inObject) + { + // lines are really just rects, but they grow in width in a sort of odd way. + // specifically, they grow the increasing coordinate on even boundaries and centered on odd + // boundaries. + SPGRect theRect; + theRect.m_Top = inObject.m_Top; + theRect.m_Bottom = inObject.m_Bottom; + theRect.m_FillColor = inObject.m_LineColor; + theRect.m_Left = inObject.m_X; + theRect.m_Right = theRect.m_Left + 1.0f; + RenderPixelObject(inProjection, theRect); + } + + void RenderPixelObject(QT3DSMat44 &inProjection, const SPGHorzLine &inObject) + { + SPGRect theRect; + theRect.m_Right = inObject.m_Right; + theRect.m_Left = inObject.m_Left; + theRect.m_FillColor = inObject.m_LineColor; + theRect.m_Bottom = inObject.m_Y; + theRect.m_Top = theRect.m_Bottom + 1.0f; + RenderPixelObject(inProjection, theRect); + } + + void Render(NVConstDataRef<SPGGraphObject *> inObjects) override + { + NVRenderContext &theRenderContext(m_RenderContext.GetRenderContext()); + theRenderContext.PushPropertySet(); + // Setup an orthographic camera that places the center at the + // lower left of the viewport. + NVRenderRectF theViewport = theRenderContext.GetViewport(); + // With no projection at all, we are going to get a square view box + // with boundaries from -1,1 in all dimensions. This is close to what we want. + theRenderContext.SetDepthTestEnabled(false); + theRenderContext.SetDepthWriteEnabled(false); + theRenderContext.SetScissorTestEnabled(false); + theRenderContext.SetBlendingEnabled(true); + theRenderContext.SetCullingEnabled(false); + // Colors are expected to be non-premultiplied, so we premultiply alpha into them at this + // point. + theRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha)); + theRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + + SCamera theCamera; + theCamera.m_Position.z = -5; + theCamera.m_ClipNear = 1.0f; + theCamera.m_ClipFar = 10.0f; + theCamera.m_Flags.SetOrthographic(true); + // Setup camera projection + theCamera.ComputeFrustumOrtho(theViewport, + QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + // Translate such that 0, 0 is lower left of screen. + NVRenderRectF theIdealViewport = theViewport; + theIdealViewport.m_X -= theViewport.m_Width / 2.0f; + theIdealViewport.m_Y -= theViewport.m_Height / 2.0f; + QT3DSMat44 theProjectionMatrix = NVRenderContext::ApplyVirtualViewportToProjectionMatrix( + theCamera.m_Projection, theViewport, theIdealViewport); + theCamera.m_Projection = theProjectionMatrix; + // Explicitly call the node's calculate global variables so that the camera doesn't attempt + // to change the projection we setup. + static_cast<SNode &>(theCamera).CalculateGlobalVariables(); + QT3DSMat44 theVPMatrix(QT3DSMat44::createIdentity()); + theCamera.CalculateViewProjectionMatrix(theVPMatrix); + + QT3DSVec4 theTest(60, 200, 0, 1); + QT3DSVec4 theResult = theVPMatrix.transform(theTest); + + (void)theTest; + (void)theResult; + + for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) { + const SPGGraphObject &theObject(*inObjects[idx]); + + switch (theObject.m_Type) { + case SGTypes::VertLine: + RenderPixelObject(theVPMatrix, static_cast<const SPGVertLine &>(theObject)); + break; + case SGTypes::HorzLine: + RenderPixelObject(theVPMatrix, static_cast<const SPGHorzLine &>(theObject)); + break; + case SGTypes::Rect: + RenderPixelObject(theVPMatrix, static_cast<const SPGRect &>(theObject)); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + theRenderContext.PopPropertySet(false); + } +}; +} + +IPixelGraphicsRenderer &IPixelGraphicsRenderer::CreateRenderer(IQt3DSRenderContext &ctx, + IStringTable &strt) +{ + return *QT3DS_NEW(ctx.GetAllocator(), SPGRenderer)(ctx, strt); +} diff --git a/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.h b/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.h new file mode 100644 index 0000000..b37cb26 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PIXEL_GRAPHICS_RENDERER_H +#define QT3DS_RENDER_PIXEL_GRAPHICS_RENDERER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSDataRef.h" + +namespace qt3ds { +namespace render { + + // Pixel graphics are graphics described in pixels. + // Colors are expected to be non-premultiplied, we use ROP + // hardware to do the alpha multiply into the color. + class IPixelGraphicsRenderer : public NVRefCounted + { + public: + // Renders the node to the current viewport. + virtual void Render(NVConstDataRef<SPGGraphObject *> inObjects) = 0; + + static IPixelGraphicsRenderer &CreateRenderer(IQt3DSRenderContext &ctx, IStringTable &strt); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.cpp b/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.cpp new file mode 100644 index 0000000..fe32437 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPixelGraphicsTypes.h" + +using namespace qt3ds; +using namespace qt3ds::render; + +SPGGraphObject::SPGGraphObject(SGTypes::Enum inType) + : m_Type(inType) +{ +} + +SPGRect::SPGRect() + : SPGGraphObject(SGTypes::Rect) + , m_Left(0) + , m_Top(0) + , m_Right(0) + , m_Bottom(0) + , m_FillColor(0, 0, 0, 0) +{ +} + +SPGVertLine::SPGVertLine() + : SPGGraphObject(SGTypes::VertLine) + , m_X(0) + , m_Top(0) + , m_Bottom(0) + , m_LineColor(0, 0, 0, 0) +{ +} + +SPGHorzLine::SPGHorzLine() + : SPGGraphObject(SGTypes::HorzLine) + , m_Y(0) + , m_Left(0) + , m_Right(0) + , m_LineColor(0, 0, 0, 0) +{ +} diff --git a/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.h b/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.h new file mode 100644 index 0000000..2c154c1 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPixelGraphicsTypes.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PIXEL_GRAPHICS_TYPES_H +#define QT3DS_RENDER_PIXEL_GRAPHICS_TYPES_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSVec4.h" +#include "foundation/Qt3DSMat33.h" +#include "foundation/Qt3DSOption.h" + +namespace qt3ds { +namespace render { + + // Vector graphics with no scaling are pixel aligned with 0,0 being the bottom,left of the + // screen + // with coordinates increasing to the right and up. This is opposite most window systems but it + // preserves the normal openGL assumptions about viewports and positive Y going up in general. + struct SGTypes + { + enum Enum { + UnknownVGType = 0, + Layer, + Rect, + VertLine, + HorzLine, + }; + }; + + struct SPGGraphObject + { + SGTypes::Enum m_Type; + SPGGraphObject(SGTypes::Enum inType); + }; + + struct SPGRect : public SPGGraphObject + { + QT3DSF32 m_Left; + QT3DSF32 m_Top; + QT3DSF32 m_Right; + QT3DSF32 m_Bottom; + + QT3DSVec4 m_FillColor; + + SPGRect(); + }; + + struct SPGVertLine : public SPGGraphObject + { + QT3DSF32 m_X; + QT3DSF32 m_Top; + QT3DSF32 m_Bottom; + QT3DSVec4 m_LineColor; + void SetPosition(QT3DSF32 val) { m_X = val; } + void SetStart(QT3DSF32 val) { m_Bottom = val; } + void SetStop(QT3DSF32 val) { m_Top = val; } + + SPGVertLine(); + }; + + struct SPGHorzLine : public SPGGraphObject + { + QT3DSF32 m_Y; + QT3DSF32 m_Left; + QT3DSF32 m_Right; + QT3DSVec4 m_LineColor; + void SetPosition(QT3DSF32 val) { m_Y = val; } + void SetStart(QT3DSF32 val) { m_Left = val; } + void SetStop(QT3DSF32 val) { m_Right = val; } + + SPGHorzLine(); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderPlugin.cpp b/src/runtimerender/Qt3DSRenderPlugin.cpp new file mode 100644 index 0000000..e4e821f --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPlugin.cpp @@ -0,0 +1,936 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPlugin.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/SerializationTypes.h" +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSSystem.h" +#include "foundation/FileTools.h" +#include "render/Qt3DSRenderContext.h" +#include "StringTools.h" +#include "Qt3DSRenderPluginPropertyValue.h" +#include "Qt3DSRenderInputStreamFactory.h" + +#if defined(QT3DS_WINDOWS) +#include "windows/DynamicLibLoader.h" +#elif defined(QT3DS_ANDROID) +#include "android/DynamicLibLoader.h" +#elif defined(QT3DS_LINUX) +#include "linux/DynamicLibLoader.h" +#elif defined(QT3DS_APPLE) +#include "macos/DynamicLibLoader.h" +#elif defined(QT3DS_QNX) +#include "qnx/DynamicLibLoader.h" +#else +#error "Must define an operating system type (QT3DS_WINDOWS, QT3DS_ANDROID, QT3DS_LINUX, QT3DS_APPLE, QT3DS_QNX)" +#endif + +using namespace qt3ds::render; + +namespace { +// Legacy definitions... +// API version 1 definitions +typedef struct _RenderPluginSurfaceDescriptionV1 +{ + long m_Width; + long m_Height; + enum QT3DSRenderPluginDepthTypes m_DepthBuffer; + enum QT3DSRenderPluginTextureTypes m_ColorBuffer; + TBool m_HasStencilBuffer; +} TRenderPluginSurfaceDescriptionV1; + +typedef TNeedsRenderResult (*TNeedsRenderFunctionV1)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescriptionV1 surface, + TVec2 presScaleFactor); + +typedef void (*TRenderFunctionV1)(TRenderPluginClassPtr cls, TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescriptionV1 surface, + TVec2 presScaleFactor, + QT3DSRenderPluginColorClearState inClearColorBuffer); + +// End API version 1 definitions + +TRenderPluginSurfaceDescription ToCInterface(const SOffscreenRendererEnvironment &env) +{ + TRenderPluginSurfaceDescription retval; + retval.m_Width = (long)env.m_Width; + retval.m_Height = (long)env.m_Height; + retval.m_ColorBuffer = static_cast<QT3DSRenderPluginTextureTypes>(env.m_Format); + retval.m_DepthBuffer = static_cast<QT3DSRenderPluginDepthTypes>(env.m_Depth); + retval.m_HasStencilBuffer = env.m_Stencil ? TTRUE : TFALSE; + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelNoMSAA; + // note no supersampling AA support for plugins + // we fall back to 4xMSAA + switch (env.m_MSAAMode) { + case AAModeValues::X2: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelTwo; + break; + case AAModeValues::SSAA: + case AAModeValues::X4: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelFour; + break; + case AAModeValues::X8: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelEight; + break; + default: + QT3DS_ASSERT(false); + // fallthrough intentional. + case AAModeValues::NoAA: + break; + }; + return retval; +} + +TRenderPluginSurfaceDescriptionV1 ToCInterfaceV1(const SOffscreenRendererEnvironment &env) +{ + TRenderPluginSurfaceDescriptionV1 retval; + retval.m_Width = (long)env.m_Width; + retval.m_Height = (long)env.m_Height; + retval.m_ColorBuffer = static_cast<QT3DSRenderPluginTextureTypes>(env.m_Format); + retval.m_DepthBuffer = static_cast<QT3DSRenderPluginDepthTypes>(env.m_Depth); + retval.m_HasStencilBuffer = env.m_Stencil ? TTRUE : TFALSE; + return retval; +} + +TVec2 ToCInterface(const QT3DSVec2 &item) +{ + TVec2 retval = { item.x, item.y }; + return retval; +} + +QT3DSRenderPluginColorClearState ToCInterface(SScene::RenderClearCommand inClearCommand) +{ + switch (inClearCommand) { + case SScene::DoNotClear: + return QT3DSRenderPluginColorClearStateDoNotClear; + case SScene::AlwaysClear: + return QT3DSRenderPluginColorClearStateAlwaysClear; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case SScene::ClearIsOptional: + return QT3DSRenderPluginColorClearStateClearIsOptional; + }; +} + +class SRenderPluginPropertyData +{ + SRenderPluginPropertyValue m_Value; + bool m_Dirty; + +public: + SRenderPluginPropertyData() + : m_Dirty(false) + { + } + SRenderPluginPropertyData(const SRenderPluginPropertyData &other) + : m_Value(other.m_Value) + , m_Dirty(other.m_Dirty) + { + } + SRenderPluginPropertyData &operator=(const SRenderPluginPropertyData &other) + { + m_Value = other.m_Value; + m_Dirty = other.m_Dirty; + return *this; + } + + bool IsDirty() const + { + return m_Value.getType() != RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue + && m_Dirty; + } + void SetValue(const SRenderPluginPropertyValue &value) + { + m_Value = value; + m_Dirty = true; + } + + TRenderPluginPropertyUpdate ClearDirty(CRegisteredString inPropName) + { + m_Dirty = false; + TRenderPluginPropertyUpdate retval; + memset(&retval, 0, sizeof(TRenderPluginPropertyUpdate)); + retval.m_PropName = inPropName.c_str(); + switch (m_Value.getType()) { + case RenderPluginPropertyValueTypes::Long: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeLong; + long temp = (long)m_Value.getData<QT3DSI32>(); + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::Float: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeFloat; + float temp = m_Value.getData<QT3DSF32>(); + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::Boolean: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeLong; + long temp = m_Value.getData<bool>() ? TTRUE : TFALSE; + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::String: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeCharPtr; + const char *temp = m_Value.getData<CRegisteredString>().c_str(); + retval.m_PropertyValue = reinterpret_cast<void *>(const_cast<char *>(temp)); + } break; + default: + QT3DS_ASSERT(false); + } + return retval; + } +}; + +typedef nvvector<SRenderPluginPropertyData> TPropertyValueList; + +struct IInternalPluginClass : public IRenderPluginClass +{ + virtual void PushUpdates(TRenderPluginInstancePtr instance, + TPropertyValueList &propertyValues) = 0; + virtual void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer, + TPropertyValueList &propertyValues) = 0; + virtual QT3DSI32 GetAPIVersion() = 0; +}; + +static NVRenderTextureFormats::Enum ToTextureFormat(QT3DSRenderPluginTextureTypes inTextureType) +{ + switch (inTextureType) { + default: + case QT3DSRenderPluginTextureTypeRGBA8: + return NVRenderTextureFormats::RGBA8; + case QT3DSRenderPluginTextureTypeRGB8: + return NVRenderTextureFormats::RGB8; + case QT3DSRenderPluginTextureTypeRGB565: + return NVRenderTextureFormats::RGB565; + case QT3DSRenderPluginTextureTypeRGBA5551: + return NVRenderTextureFormats::RGBA5551; + } +} + +static OffscreenRendererDepthValues::Enum ToDepthValue(QT3DSRenderPluginDepthTypes inType) +{ + switch (inType) { + default: + case QT3DSRenderPluginDepthTypeDepth16: + return OffscreenRendererDepthValues::Depth16; + case QT3DSRenderPluginDepthTypeDepth24: + return OffscreenRendererDepthValues::Depth24; + case QT3DSRenderPluginDepthTypeDepth32: + return OffscreenRendererDepthValues::Depth32; + } +} + +static AAModeValues::Enum ToAAMode(QT3DSRenderPluginMSAALevel inMode) +{ + switch (inMode) { + case QT3DSRenderPluginMSAALevelTwo: + return AAModeValues::X2; + case QT3DSRenderPluginMSAALevelFour: + return AAModeValues::X4; + case QT3DSRenderPluginMSAALevelEight: + return AAModeValues::X8; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case QT3DSRenderPluginMSAALevelNoMSAA: + return AAModeValues::NoAA; + } +} + +struct InstanceImpl : public IRenderPluginInstance +{ + NVFoundationBase &m_Foundation; + TRenderPluginInstancePtr m_Instance; + TRenderPluginClass m_Class; + NVScopedRefCounted<IInternalPluginClass> m_Owner; + CRegisteredString m_RendererType; + // Backing store of property values + nvvector<SRenderPluginPropertyData> m_PropertyValues; + bool m_Dirty; + NVRenderContext *m_RenderContext; + QT3DSI32 mRefCount; + + InstanceImpl(NVFoundationBase &fnd, TRenderPluginInstancePtr instance, TRenderPluginClass cls, + IInternalPluginClass &owner, IStringTable &strTable) + : m_Foundation(fnd) + , m_Instance(instance) + , m_Class(cls) + , m_Owner(owner) + , m_RendererType( + strTable.RegisterStr(IRenderPluginInstance::IRenderPluginOffscreenRendererType())) + , m_PropertyValues(m_Foundation.getAllocator(), "InstanceImpl::m_PropertyValues") + , m_Dirty(false) + , m_RenderContext(NULL) + , mRefCount(0) + { + } + + virtual ~InstanceImpl() { m_Class.ReleaseInstance(m_Class.m_Class, m_Instance); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void addCallback(IOffscreenRendererCallback *cb) override + { + + } + void CreateScriptProxy(script_State *state) override + { + if (m_Class.CreateInstanceScriptProxy) + m_Class.CreateInstanceScriptProxy(m_Class.m_Class, m_Instance, state); + } + + // Arbitrary const char* returned to indicate the type of this renderer + // Can be overloaded to form the basis of an RTTI type system. + // Not currently used by the rendering system. + CRegisteredString GetOffscreenRendererType() override { return m_RendererType; } + + SOffscreenRendererEnvironment GetDesiredEnvironment(QT3DSVec2 inPresentationScaleFactor) override + { + if (m_Class.QueryInstanceRenderSurface) { + QT3DSRenderPluginMSAALevel theLevel = QT3DSRenderPluginMSAALevelNoMSAA; + TRenderPluginSurfaceDescription desc = m_Class.QueryInstanceRenderSurface( + m_Class.m_Class, m_Instance, ToCInterface(inPresentationScaleFactor)); + if (m_Owner->GetAPIVersion() > 1) + theLevel = desc.m_MSAALevel; + + return SOffscreenRendererEnvironment( + (QT3DSU32)desc.m_Width, (QT3DSU32)desc.m_Height, ToTextureFormat(desc.m_ColorBuffer), + ToDepthValue(desc.m_DepthBuffer), desc.m_HasStencilBuffer ? true : false, + ToAAMode(theLevel)); + } else { + QT3DS_ASSERT(false); + } + return SOffscreenRendererEnvironment(); + } + + // Returns true of this object needs to be rendered, false if this object is not dirty + SOffscreenRenderFlags NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) override + { + if (m_Dirty) { + m_Dirty = false; + m_Owner->PushUpdates(m_Instance, m_PropertyValues); + } + if (m_Class.NeedsRenderFunction) { + if (m_Owner->GetAPIVersion() > 1) { + TNeedsRenderResult result = m_Class.NeedsRenderFunction( + m_Class.m_Class, m_Instance, ToCInterface(inEnvironment), + ToCInterface(inPresentationScaleFactor)); + return SOffscreenRenderFlags(result.HasTransparency ? true : false, + result.HasChangedSinceLastFrame ? true : false); + } else { + TNeedsRenderFunctionV1 theV1Function = + reinterpret_cast<TNeedsRenderFunctionV1>(m_Class.NeedsRenderFunction); + + TNeedsRenderResult result = + theV1Function(m_Class.m_Class, m_Instance, ToCInterfaceV1(inEnvironment), + ToCInterface(inPresentationScaleFactor)); + return SOffscreenRenderFlags(result.HasTransparency ? true : false, + result.HasChangedSinceLastFrame ? true : false); + } + } + return SOffscreenRenderFlags(true, true); + } + // Returns true if the rendered result image has transparency, or false + // if it should be treated as a completely opaque image. + // It is the IOffscreenRenderer's job to clear any buffers (color, depth, stencil) that it + // needs to. It should not assume that it's buffers are clear; + // Sometimes we scale the width and height of the main presentation in order to fit a window. + // If we do so, the scale factor tells the subpresentation renderer how much the system has + // scaled. + void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) override + { + m_RenderContext = &inRenderContext; + if (m_Class.RenderInstance) { + inRenderContext.PushPropertySet(); + if (m_Owner->GetAPIVersion() > 1) { + m_Class.RenderInstance(m_Class.m_Class, m_Instance, ToCInterface(inEnvironment), + ToCInterface(inPresentationScaleFactor), + ToCInterface(inColorBufferNeedsClear)); + } else { + TRenderFunctionV1 theV1Function = + reinterpret_cast<TRenderFunctionV1>(m_Class.RenderInstance); + theV1Function(m_Class.m_Class, m_Instance, ToCInterfaceV1(inEnvironment), + ToCInterface(inPresentationScaleFactor), + ToCInterface(inColorBufferNeedsClear)); + } + + inRenderContext.PopPropertySet(true); + } + } + + void RenderWithClear(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, + SScene::RenderClearCommand inClearBuffer, QT3DSVec4 inClearColor, + const SRenderInstanceId id) + { + Q_ASSERT(false); + } + + // Implementors should implement one of the two interfaces below. + + // If this renderer supports picking that can return graph objects + // then return an interface here. + IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId) override { return NULL; } + + // If you *don't* support the GraphObjectPickIterator interface, then you should implement this + // interface + // The system will just ask you to pick. + // If you return true, then we will assume that you swallowed the pick and will continue no + // further. + // else we will assume you did not and will continue the picking algorithm. + bool Pick(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inViewportDimensions, + const SRenderInstanceId instanceId) override + { + if (m_Class.Pick) { + if (m_RenderContext) { + m_RenderContext->PushPropertySet(); + bool retval = m_Class.Pick(m_Class.m_Class, m_Instance, ToCInterface(inMouseCoords), + ToCInterface(inViewportDimensions)) + ? true + : false; + m_RenderContext->PopPropertySet(true); + return retval; + } + } + return false; + } + + TRenderPluginInstancePtr GetRenderPluginInstance() override { return m_Instance; } + void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer) override + { + m_Dirty = true; + m_Owner->Update(updateBuffer, m_PropertyValues); + } + IRenderPluginClass &GetPluginClass() override { return *m_Owner; } +}; + +typedef eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum> TStringTypePair; + +struct PluginClassImpl : public IInternalPluginClass +{ + typedef nvhash_map<CRegisteredString, QT3DSU32> TStringIndexMap; + NVFoundationBase &m_Foundation; + IStringTable &m_StringTable; + TRenderPluginClass m_Class; + CRegisteredString m_Type; + CLoadedDynamicLibrary *m_DynamicLibrary; + nvvector<SRenderPluginPropertyDeclaration> m_RegisteredProperties; + TStringIndexMap m_ComponentNameToComponentIndexMap; + nvvector<TStringTypePair> m_FullPropertyList; + nvvector<TRenderPluginPropertyUpdate> m_UpdateBuffer; + Qt3DSString m_TempString; + QT3DSI32 m_APIVersion; + + QT3DSI32 mRefCount; + + PluginClassImpl(NVFoundationBase &fnd, IStringTable &strTable, TRenderPluginClass inClass, + CRegisteredString inType, CLoadedDynamicLibrary *inLibrary) + : m_Foundation(fnd) + , m_StringTable(strTable) + , m_Class(inClass) + , m_Type(inType) + , m_DynamicLibrary(inLibrary) + , m_RegisteredProperties(m_Foundation.getAllocator(), + "PluginClassImpl::m_RegisteredProperties") + , m_ComponentNameToComponentIndexMap(m_Foundation.getAllocator(), + "PluginClassImpl::m_ComponentNameToComponentIndexMap") + , m_FullPropertyList(m_Foundation.getAllocator(), "PluginClassImpl::m_FullPropertyList") + , m_UpdateBuffer(m_Foundation.getAllocator(), "PluginClassImpl::m_UpdateBuffer") + , m_APIVersion(m_Class.GetRenderPluginAPIVersion(m_Class.m_Class)) + , mRefCount(0) + { + } + ~PluginClassImpl() + { + if (m_Class.ReleaseClass) + m_Class.ReleaseClass(m_Class.m_Class); + if (m_DynamicLibrary) + NVDelete(m_Foundation.getAllocator(), m_DynamicLibrary); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + NVScopedRefCounted<IRenderPluginInstance> CreateInstance() override + { + if (m_Class.CreateInstance) { + TRenderPluginInstancePtr instance = + m_Class.CreateInstance(m_Class.m_Class, m_Type.c_str()); + if (instance) { + InstanceImpl *retval = QT3DS_NEW(m_Foundation.getAllocator(), InstanceImpl)( + m_Foundation, instance, m_Class, *this, m_StringTable); + return retval; + } + } + return NVScopedRefCounted<IRenderPluginInstance>(); + } + + QT3DSI32 GetAPIVersion() override { return m_APIVersion; } + + void AddFullPropertyType(const char *name, RenderPluginPropertyValueTypes::Enum inType) + { + QT3DSU32 itemIndex = (QT3DSU32)m_FullPropertyList.size(); + CRegisteredString regName = m_StringTable.RegisterStr(name); + bool inserted = + m_ComponentNameToComponentIndexMap.insert(eastl::make_pair(regName, itemIndex)).second; + if (inserted) { + m_FullPropertyList.push_back(eastl::make_pair(regName, inType)); + } else { + // Duplicate property declaration. + QT3DS_ASSERT(false); + } + } + + void AddFullPropertyType(const char *name, const char *extension, + RenderPluginPropertyValueTypes::Enum inType) + { + m_TempString.assign(name); + if (!isTrivial(extension)) { + m_TempString.append("."); + m_TempString.append(extension); + } + AddFullPropertyType(m_TempString.c_str(), inType); + } + + void RegisterProperty(const SRenderPluginPropertyDeclaration &dec) override + { + QT3DSU32 startOffset = (QT3DSU32)m_FullPropertyList.size(); + + switch (dec.m_Type) { + + case SRenderPluginPropertyTypes::Vector2: + AddFullPropertyType(dec.m_Name, "x", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "y", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Color: + AddFullPropertyType(dec.m_Name, "r", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "g", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "b", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Vector3: + AddFullPropertyType(dec.m_Name, "x", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "y", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "z", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Boolean: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Boolean); + break; + case SRenderPluginPropertyTypes::Float: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Long: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Long); + break; + case SRenderPluginPropertyTypes::String: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::String); + break; + default: + QT3DS_ASSERT(false); + break; + } + m_RegisteredProperties.push_back(dec); + m_RegisteredProperties.back().m_StartOffset = startOffset; + } + + NVConstDataRef<SRenderPluginPropertyDeclaration> GetRegisteredProperties() override + { + return m_RegisteredProperties; + } + + SRenderPluginPropertyDeclaration GetPropertyDeclaration(CRegisteredString inPropName) override + { + for (QT3DSU32 idx = 0, end = m_RegisteredProperties.size(); idx < end; ++idx) { + if (m_RegisteredProperties[idx].m_Name == inPropName) + return m_RegisteredProperties[idx]; + } + QT3DS_ASSERT(false); + return SRenderPluginPropertyDeclaration(); + } + + // From which you can get the property name breakdown + virtual eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum> + GetPropertyValueInfo(QT3DSU32 inIndex) override + { + if (inIndex < m_FullPropertyList.size()) + return m_FullPropertyList[inIndex]; + QT3DS_ASSERT(false); + return eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum>( + CRegisteredString(), RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue); + } + + void PushUpdates(TRenderPluginInstancePtr instance, TPropertyValueList &propertyValues) override + { + m_UpdateBuffer.clear(); + for (QT3DSU32 idx = 0, end = propertyValues.size(); idx < end; ++idx) { + SRenderPluginPropertyData &theData(propertyValues[idx]); + if (theData.IsDirty()) + m_UpdateBuffer.push_back(theData.ClearDirty(m_FullPropertyList[idx].first)); + } + if (m_Class.UpdateInstance) + m_Class.UpdateInstance(m_Class.m_Class, instance, m_UpdateBuffer.data(), + (long)m_UpdateBuffer.size()); + } + + void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer, + TPropertyValueList &propertyValues) override + { + for (QT3DSU32 idx = 0, end = updateBuffer.size(); idx < end; ++idx) { + const SRenderPropertyValueUpdate &update = updateBuffer[idx]; + TStringIndexMap::iterator iter = + m_ComponentNameToComponentIndexMap.find(update.m_PropertyName); + if (iter == m_ComponentNameToComponentIndexMap.end()) { + QT3DS_ASSERT(false); + continue; + } + + QT3DSU32 propIndex = iter->second; + if (update.m_Value.getType() != m_FullPropertyList[propIndex].second) { + QT3DS_ASSERT(false); + continue; + } + if (propIndex >= propertyValues.size()) + propertyValues.resize(propIndex + 1); + propertyValues[propIndex].SetValue(update.m_Value); + } + } +}; + +struct PluginInstanceKey +{ + CRegisteredString m_Path; + void *m_InstanceKey; + PluginInstanceKey(CRegisteredString p, void *ik) + : m_Path(p) + , m_InstanceKey(ik) + { + } + bool operator==(const PluginInstanceKey &rhs) const + { + return m_Path == rhs.m_Path && m_InstanceKey == rhs.m_InstanceKey; + } +}; +} + +namespace eastl { +template <> +struct hash<PluginInstanceKey> +{ + size_t operator()(const PluginInstanceKey &k) const + { + return hash<CRegisteredString>()(k.m_Path) + ^ hash<size_t>()(reinterpret_cast<size_t>(k.m_InstanceKey)); + } + bool operator()(const PluginInstanceKey &lhs, const PluginInstanceKey &rhs) const + { + return lhs.m_Path == rhs.m_Path && lhs.m_InstanceKey == rhs.m_InstanceKey; + } +}; +} + +namespace { + +struct SLoadedPluginData +{ + CRegisteredString m_PluginPath; + eastl::vector<SRenderPluginPropertyDeclaration> m_Properties; +}; + +typedef eastl::vector<SLoadedPluginData> TLoadedPluginDataList; + +struct PluginManagerImpl : public IRenderPluginManager, public IRenderPluginManagerCore +{ + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<IRenderPluginClass>> TLoadedClassMap; + typedef nvhash_map<PluginInstanceKey, NVScopedRefCounted<IRenderPluginInstance>> TInstanceMap; + NVFoundationBase &m_Foundation; + IStringTable &m_StringTable; + TLoadedClassMap m_LoadedClasses; + TInstanceMap m_Instances; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + IInputStreamFactory &m_InputStreamFactory; + QT3DSI32 mRefCount; + TStr m_DllDir; + TLoadedPluginDataList m_LoadedPluginData; + + PluginManagerImpl(NVFoundationBase &fnd, IStringTable &st, IInputStreamFactory &inFactory) + : m_Foundation(fnd) + , m_StringTable(st) + , m_LoadedClasses(fnd.getAllocator(), "PluginManagerImpl::m_LoadedClasses") + , m_Instances(fnd.getAllocator(), "PluginManagerImpl::m_Instances") + , m_InputStreamFactory(inFactory) + , mRefCount(0) + , m_DllDir(ForwardingAllocator(fnd.getAllocator(), "PluginManagerImpl::m_DllDir")) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + IRenderPluginClass *GetRenderPlugin(CRegisteredString inRelativePath) override + { + TLoadedClassMap::iterator iter = m_LoadedClasses.find(inRelativePath); + if (iter != m_LoadedClasses.end()) + return iter->second; + + return NVScopedRefCounted<IRenderPluginClass>(); + } + + IRenderPluginClass *GetOrCreateRenderPlugin(CRegisteredString inRelativePath) override + { + TLoadedClassMap::iterator iter = m_LoadedClasses.find(inRelativePath); + if (iter != m_LoadedClasses.end()) { + return iter->second; + } + + // We insert right here to keep us from going down this path potentially for every instance. + iter = + m_LoadedClasses + .insert(eastl::make_pair(inRelativePath, NVScopedRefCounted<IRenderPluginClass>())) + .first; + eastl::string xmlDir, fname, extension; + + CFileTools::Split(inRelativePath.c_str(), xmlDir, fname, extension); + + eastl::string sharedLibrary(xmlDir); + eastl::string subdir(qt3ds::foundation::System::getPlatformGLStr()); + eastl::string libdir; + eastl::string libpath; + + CFileTools::CombineBaseAndRelative(xmlDir.c_str(), subdir.c_str(), libdir); + CFileTools::CombineBaseAndRelative(libdir.c_str(), fname.c_str(), libpath); +#ifdef _DEBUG + libpath.append("d"); +#endif + libpath.append(qt3ds::foundation::System::g_DLLExtension); + eastl::string loadPath; + if (m_DllDir.size()) { + // Then we have to copy the dll to the dll directory before loading because the + // filesystem + // the plugin is on may not be executable. + eastl::string targetFile; + CFileTools::CombineBaseAndRelative(m_DllDir.c_str(), fname.c_str(), targetFile); +#ifdef _DEBUG + targetFile.append("d"); +#endif + targetFile.append(qt3ds::foundation::System::g_DLLExtension); + + qCInfo(TRACE_INFO, "Copying plugin shared library from %s to %s", + libpath.c_str(), targetFile.c_str()); + + // try to open the library. + NVScopedRefCounted<IRefCountedInputStream> theStream = + m_InputStreamFactory.GetStreamForFile(libpath.c_str()); + if (!theStream) { + qCCritical(INVALID_OPERATION, "Failed to load render plugin %s", + libpath.c_str()); + return NULL; + } + CFileSeekableIOStream outStream(targetFile.c_str(), FileWriteFlags()); + if (!outStream.IsOpen()) { + qCCritical(INVALID_OPERATION, "Failed to load render plugin %s", + targetFile.c_str()); + return NULL; + } + + QT3DSU8 buf[1024] = { 0 }; + for (QT3DSU32 len = theStream->Read(toDataRef(buf, 1024)); len; + len = theStream->Read(toDataRef(buf, 1024))) { + outStream.Write(toDataRef(buf, len)); + } + loadPath = targetFile; + } else { + QString path; + m_InputStreamFactory.GetPathForFile(libpath.c_str(), path); + loadPath = path.toUtf8().data(); + } + CLoadedDynamicLibrary *library = NULL; + TRenderPluginClass newPluginClass; + memSet(&newPluginClass, 0, sizeof(newPluginClass)); + + // Do not load plugin dlls during compilation steps or when we don't have a valid render + // context. + // They may try opengl access at some point and that would end in disaster during binary + // save steps. + if ((QT3DSU32)m_RenderContext->GetRenderContextType() != NVRenderContextValues::NullContext) { + library = CLoadedDynamicLibrary::Create(loadPath.c_str(), m_Foundation); + if (!library) { + // try loading it from the system instead of from this specific path. This means do + // not use any extensions or any special + // sauce. + loadPath = fname; +#ifdef _DEBUG + loadPath.append("d"); +#endif + library = CLoadedDynamicLibrary::Create(loadPath.c_str(), m_Foundation); + } + } + + if (library) { + TCreateRenderPluginClassFunction CreateClass = + reinterpret_cast<TCreateRenderPluginClassFunction>( + library->FindFunction("CreateRenderPlugin")); + if (CreateClass) { + newPluginClass = CreateClass(fname.c_str()); + if (newPluginClass.m_Class) { + // Check that the required functions are there. + if (newPluginClass.CreateInstance == NULL + || newPluginClass.QueryInstanceRenderSurface == NULL + || newPluginClass.RenderInstance == NULL + || newPluginClass.ReleaseInstance == NULL + || newPluginClass.ReleaseClass == NULL) { + if (newPluginClass.ReleaseClass) + newPluginClass.ReleaseClass(newPluginClass.m_Class); + qCCritical(INVALID_OPERATION, + "Failed to load render plugin: %s, required functions " + "missing. Required functions are:" + "CreateInstance, QueryInstanceRenderSurface, " + "RenderInstance, ReleaseInstance, ReleaseClass", + inRelativePath.c_str()); + NVDelete(m_Foundation.getAllocator(), library); + memSet(&newPluginClass, 0, sizeof(newPluginClass)); + } + } + } + } + if (newPluginClass.m_Class) { + PluginClassImpl *retval = QT3DS_NEW(m_Foundation.getAllocator(), PluginClassImpl)( + m_Foundation, m_StringTable, newPluginClass, + m_StringTable.RegisterStr(fname.c_str()), library); + + iter->second = retval; + if (newPluginClass.InitializeClassGLResources) { + m_RenderContext->PushPropertySet(); + newPluginClass.InitializeClassGLResources(newPluginClass.m_Class, loadPath.c_str()); + m_RenderContext->PopPropertySet(true); + } + return iter->second; + } + return NULL; + } + + void SetDllDir(const char *inDllDir) override { m_DllDir.assign(nonNull(inDllDir)); } + + IRenderPluginInstance *GetOrCreateRenderPluginInstance(CRegisteredString inRelativePath, + void *inKey) override + { + PluginInstanceKey theKey(inRelativePath, inKey); + TInstanceMap::iterator iter = m_Instances.find(theKey); + if (iter == m_Instances.end()) { + IRenderPluginClass *theClass = GetOrCreateRenderPlugin(inRelativePath); + NVScopedRefCounted<IRenderPluginInstance> theInstance; + if (theClass) + theInstance = theClass->CreateInstance(); + + iter = m_Instances.insert(eastl::make_pair(theKey, theInstance)).first; + } + return iter->second.mPtr; + } + + void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t * /*inProjectDir*/) const override + { + QT3DSU32 numClasses = m_LoadedClasses.size(); + ioBuffer.write(numClasses); + for (TLoadedClassMap::const_iterator iter = m_LoadedClasses.begin(), + end = m_LoadedClasses.end(); + iter != end; ++iter) { + CRegisteredString saveStr = iter->first; + saveStr.Remap(inRemapMap); + ioBuffer.write(saveStr); + if (iter->second) { + NVConstDataRef<SRenderPluginPropertyDeclaration> theProperties = + const_cast<IRenderPluginClass &>((*iter->second)).GetRegisteredProperties(); + ioBuffer.write(theProperties.size()); + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + SRenderPluginPropertyDeclaration theDec(theProperties[idx]); + theDec.m_Name.Remap(inRemapMap); + ioBuffer.write(theDec); + } + } else + ioBuffer.write((QT3DSU32)0); + } + } + + void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t * /*inProjectDir*/) override + { + qt3ds::render::SDataReader theReader(inData.begin(), inData.end()); + QT3DSU32 numClasses = theReader.LoadRef<QT3DSU32>(); + ForwardingAllocator alloc(m_Foundation.getAllocator(), "tempstrings"); + qt3ds::foundation::TStr workStr(alloc); + nvvector<SRenderPluginPropertyDeclaration> propertyBuffer(m_Foundation.getAllocator(), + "tempprops"); + for (QT3DSU32 classIdx = 0; classIdx < numClasses; ++classIdx) { + CRegisteredString classPath = theReader.LoadRef<CRegisteredString>(); + classPath.Remap(inStrDataBlock); + QT3DSU32 numProperties = theReader.LoadRef<QT3DSU32>(); + propertyBuffer.clear(); + for (QT3DSU32 propIdx = 0; propIdx < numProperties; ++propIdx) { + propertyBuffer.push_back(theReader.LoadRef<SRenderPluginPropertyDeclaration>()); + propertyBuffer.back().m_Name.Remap(inStrDataBlock); + } + m_LoadedPluginData.push_back(SLoadedPluginData()); + m_LoadedPluginData.back().m_PluginPath = classPath; + m_LoadedPluginData.back().m_Properties.assign(propertyBuffer.begin(), + propertyBuffer.end()); + } + } + IRenderPluginManager &GetRenderPluginManager(NVRenderContext &rc) override + { + m_RenderContext = rc; + for (QT3DSU32 idx = 0, end = m_LoadedPluginData.size(); idx < end; ++idx) { + // Now we can attempt to load the class. + IRenderPluginClass *theClass = + GetOrCreateRenderPlugin(m_LoadedPluginData[idx].m_PluginPath); + if (theClass) { + eastl::vector<SRenderPluginPropertyDeclaration> &propertyBuffer( + m_LoadedPluginData[idx].m_Properties); + for (QT3DSU32 propIdx = 0, propEnd = propertyBuffer.size(); propIdx < propEnd; + ++propIdx) { + theClass->RegisterProperty(propertyBuffer[propIdx]); + } + } + } + m_LoadedPluginData.clear(); + return *this; + } +}; +} + +IRenderPluginManagerCore &IRenderPluginManagerCore::Create(NVFoundationBase &inFoundation, + IStringTable &strTable, + IInputStreamFactory &inFactory) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), PluginManagerImpl)(inFoundation, strTable, + inFactory); +} diff --git a/src/runtimerender/Qt3DSRenderPlugin.h b/src/runtimerender/Qt3DSRenderPlugin.h new file mode 100644 index 0000000..eb71f84 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPlugin.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PLUGIN_H +#define QT3DS_RENDER_PLUGIN_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderPluginCInterface.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "EASTL/utility.h" + +namespace qt3ds { +namespace render { + + // UICRenderPluginPropertyValue.h + struct SRenderPropertyValueUpdate; + + class IRenderPluginInstance : public IOffscreenRenderer + { + protected: + virtual ~IRenderPluginInstance() {} + public: + static const char *IRenderPluginOffscreenRendererType() { return "IRenderPluginInstance"; } + // If this render plugin has an instance ptr, get it. + virtual TRenderPluginInstancePtr GetRenderPluginInstance() = 0; + virtual void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer) = 0; + virtual IRenderPluginClass &GetPluginClass() = 0; + virtual void CreateScriptProxy(script_State *state) = 0; + }; + struct RenderPluginPropertyValueTypes + { + enum Enum { + NoRenderPluginPropertyValue = 0, + Boolean, + Long, + Float, + String, + }; + }; + + struct SRenderPluginPropertyTypes + { + enum Enum { + UnknownRenderPluginPropertyType = 0, + Float, + Vector3, + Vector2, + Color, + Boolean, + Long, + String, + }; + }; + + struct SRenderPluginPropertyDeclaration + { + CRegisteredString m_Name; + SRenderPluginPropertyTypes::Enum m_Type; + // Filled in by the class, ignored if set on registered property + QT3DSU32 m_StartOffset; + SRenderPluginPropertyDeclaration() + : m_Type(SRenderPluginPropertyTypes::UnknownRenderPluginPropertyType) + { + } + SRenderPluginPropertyDeclaration(CRegisteredString n, SRenderPluginPropertyTypes::Enum t) + : m_Name(n) + , m_Type(t) + , m_StartOffset(0) + { + } + }; + + class IRenderPluginClass : public NVRefCounted + { + protected: + virtual ~IRenderPluginClass() {} + public: + virtual NVScopedRefCounted<IRenderPluginInstance> CreateInstance() = 0; + virtual void RegisterProperty(const SRenderPluginPropertyDeclaration &dec) = 0; + virtual NVConstDataRef<SRenderPluginPropertyDeclaration> GetRegisteredProperties() = 0; + // The declaration contains an offset + virtual SRenderPluginPropertyDeclaration + GetPropertyDeclaration(CRegisteredString inPropName) = 0; + // From which you can get the property name breakdown + virtual eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum> + GetPropertyValueInfo(QT3DSU32 inIndex) = 0; + }; + + class IRenderPluginManager; + + class IRenderPluginManagerCore : public NVRefCounted + { + public: + virtual void SetDllDir(const char *inDllDir) = 0; + virtual void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t *inProjectDir) = 0; + virtual IRenderPluginManager &GetRenderPluginManager(NVRenderContext &rc) = 0; + + static IRenderPluginManagerCore &Create(NVFoundationBase &inFoundation, + IStringTable &strTable, + IInputStreamFactory &inFactory); + }; + + class IRenderPluginManager : public NVRefCounted + { + public: + virtual IRenderPluginClass *GetRenderPlugin(CRegisteredString inRelativePath) = 0; + virtual IRenderPluginClass *GetOrCreateRenderPlugin(CRegisteredString inRelativePath) = 0; + // Map a render plugin instance to this key. The instance's lifetime is managed by the + // manager so a client does not + // need to manage it. + virtual IRenderPluginInstance * + GetOrCreateRenderPluginInstance(CRegisteredString inRelativePath, void *inKey) = 0; + + virtual void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t *inProjectDir) const = 0; + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderPluginCInterface.h b/src/runtimerender/Qt3DSRenderPluginCInterface.h new file mode 100644 index 0000000..ec7481a --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPluginCInterface.h @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_OBJECT_RENDER_PLUGIN_H +#define QT3DS_OBJECT_RENDER_PLUGIN_H + +/* + * Below are the definitions required in order to write a render plugin for UIComposer. + * Please note that calling anything related to opengl is explicitly not allowed except + * during either the class gl resource initialization function or during render. Calling into + * openGL and especially changing GL state during any other function may produce corrupt + *rendering. + */ + +#ifdef _cplusplus +#extern "C" { +#endif + +enum QT3DSRenderPluginPropertyTypes { + QT3DSRenderPluginPropertyTypeNone = 0, + QT3DSRenderPluginPropertyTypeLong = 1, + QT3DSRenderPluginPropertyTypeFloat = 2, + QT3DSRenderPluginPropertyTypeCharPtr = 3, +}; + +enum QT3DSRenderPluginDepthTypes { + QT3DSRenderPluginDepthTypeNoDepthBuffer = 0, + QT3DSRenderPluginDepthTypeDepth16, // 16 bit depth buffer + QT3DSRenderPluginDepthTypeDepth24, // 24 bit depth buffer + QT3DSRenderPluginDepthTypeDepth32, // 32 bit depth buffer +}; + +enum QT3DSRenderPluginTextureTypes { + QT3DSRenderPluginTextureTypeNoTexture = 0, + QT3DSRenderPluginTextureTypeRGBA8, // 32 bit format + QT3DSRenderPluginTextureTypeRGB8, // 24 bit format + QT3DSRenderPluginTextureTypeRGB565, // 16 bit format + QT3DSRenderPluginTextureTypeRGBA5551, // 16 bit format +}; + +enum QT3DSRenderPluginColorClearState { + QT3DSRenderPluginColorClearStateClearIsOptional = 0, + QT3DSRenderPluginColorClearStateDoNotClear, + QT3DSRenderPluginColorClearStateAlwaysClear, +}; + +enum QT3DSRenderPluginMSAALevel { + QT3DSRenderPluginMSAALevelNoMSAA = 0, // no MSAA, one also works. + QT3DSRenderPluginMSAALevelTwo = 2, // 2 samples + QT3DSRenderPluginMSAALevelFour = 4, // 4 samples + QT3DSRenderPluginMSAALevelEight = 8, // 8 samples +}; + +typedef long TBool; +#define TTRUE 1 +#define TFALSE 0 + +#define QT3DS_CURRENT_RENDER_PLUGIN_API_VERSION 2 + +typedef void *TRenderPluginInstancePtr; +typedef void *TRenderPluginClassPtr; + +// We will pass the componentized properties to the instance ptr. +typedef struct _RenderPluginPropertyUpdate +{ + const char *m_PropName; + enum QT3DSRenderPluginPropertyTypes m_PropertyType; + + // Is either a float or a long or a const char* depending on the property type. + // for specify types of properties, example code would be: + // float value = *((float*)&update.m_PropertyValue) + // long value = *((long*)&update.m_PropertyValue) + // char* value = (char*)update.m_PropertyValue + void *m_PropertyValue; +} TRenderPluginPropertyUpdate; + +typedef struct _RenderPluginSurfaceDescription +{ + long m_Width; + long m_Height; + enum QT3DSRenderPluginDepthTypes m_DepthBuffer; + enum QT3DSRenderPluginTextureTypes m_ColorBuffer; + TBool m_HasStencilBuffer; + QT3DSRenderPluginMSAALevel m_MSAALevel; +} TRenderPluginSurfaceDescription; + +typedef struct _TVec2 +{ + float x; + float y; +} TVec2; + +typedef struct _NeedsRenderResult +{ + TBool HasChangedSinceLastFrame; + TBool HasTransparency; +} TNeedsRenderResult; + +struct script_State; + +/* + * Create a new instance object. Typename is the name of the plugin file, so for example + * gears.plugin generates 'gears' as a type name. + * + * Required API function. + * + */ +typedef TRenderPluginInstancePtr (*TCreateInstanceFunction)(TRenderPluginClassPtr cls, + const char *inTypeName); + +typedef void (*TCreateInstanceScriptProxy)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr insPtr, + struct script_State *state); + +/* + * Update the plugin instance with a list of property updates. Properties are broken down by + *component so for example + * a color property named leftColor will be broken down into 'leftColor.r', 'leftColor.g', + *'leftColor.b'. Vector + * properties are broken down into x,y,z components. The property string has a void* member + *that is the actual value + * or in a charPtr property's case it is the char*. + * Please see the comments for m_PropertyValue member of TRenderPluginPropertyUpdate struct. + * + * Optional API function. + */ +typedef void (*TUpdateInstanceFunction)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance, + TRenderPluginPropertyUpdate *updates, long numUpdates); + +/* + * Query used when the plugin is rendering to an image. Should return the desired + *specifications of the plugins + * render target. + * presScaleFactor - the presentation scale factor when the user has requested scale to fit to + *be used for the + * presentation. + * + * Required API function. + */ +typedef TRenderPluginSurfaceDescription (*TSurfaceQueryFunction)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance, + TVec2 presScaleFactor); + +/* + * Query used by the rendering system. Should return true if the plugin will render something + *different than it did + * the last time it rendered. This is used so that we can cache render results and also so that we + *can trigger the + * progressive AA algorithm in the case where nothing has changed. + * + * presScaleFactor - the presentation scale factor when the user has requested scale to fit to be + *used for the + * presentation. + * + * OpenGL state may be changed in this function. + * + * Optional API function, returns true by default. + */ +typedef TNeedsRenderResult (*TNeedsRenderFunction)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescription surface, + TVec2 presScaleFactor); + +/* + * Render plugin data. + * Do not assume the surface requested is the surface given; for some cases it will be but if + *the system has deemed it + * appropriate to render the plugin directly to the back buffer then the surface description + *presented could differ by + * quite a bit. + * + * presScaleFactor - is the presentation scale factor when the user has requested scale to fit + *to be used for the + * presentation. + * inClearColorBuffer - True if the plugin needs to clear the color buffer (when rendering to + *texture) else false + * (when rendering to back buffer). + * + * Function should return 'UICTRUE' the image produced by rendering contains transparency; + *either every pixel wasn't + * written to or it is desired for the plugin to blend with background objects. Else should + *return UICFALSE. + * + * Required API function. + */ +typedef void (*TRenderFunction)(TRenderPluginClassPtr cls, TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescription surface, TVec2 presScaleFactor, + QT3DSRenderPluginColorClearState inClearColorBuffer); + +/* + * Pick - handle a mouse pick into the plugin. + * Returns true if the pick was consumed, false otherwise. + * + * Option API function. + */ +typedef TBool (*TPickFunction)(TRenderPluginClassPtr cls, TRenderPluginInstancePtr instance, + TVec2 inMouse, TVec2 inViewport); + +/* + * Release a given instance of the plugin. + * + * Required API function. + */ +typedef void (*TReleaseInstanceFunction)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance); + +/* + * Get the plugin API version. This allows the runtime to account for API changes over time or + * refuse to load the plugin. Plugins should return QT3DS_CURRENT_RENDER_PLUGIN_API_VERSION + * + * Required API function. + */ +typedef long (*TGetAPIVersionFunction)(TRenderPluginClassPtr cls); + +/* + * Initialize the resources for the class. Implementing this allows UIComposer to move + * expensive initialization outside of the actual presentation run, thus allowing for + * a smoother experience during the presentation at the cost of longer startup times. + * + * - plugin path is the path to the .plugin xml file so that clients can find resources + * specific to their plugin relative to their .plugin file. + * + * OpenGL state may be changed in this function. + * + * Optional API function. + */ +typedef void (*TInitializeClassGLResourcesFunction)(TRenderPluginClassPtr cls, + const char *pluginPath); + +/* + * Release the class allocated with the create proc provided in the shared library. + * + * Required API function. + */ +typedef void (*TReleaseClassFunction)(TRenderPluginClassPtr cls); + +/* + * Structure returned form the create class function. Unimplemented functions should be left + *NULL. + */ +typedef struct _RenderPluginClass +{ + TRenderPluginClassPtr m_Class; + + TGetAPIVersionFunction GetRenderPluginAPIVersion; + TInitializeClassGLResourcesFunction InitializeClassGLResources; + TReleaseClassFunction ReleaseClass; + + TCreateInstanceFunction CreateInstance; + TCreateInstanceScriptProxy CreateInstanceScriptProxy; + TUpdateInstanceFunction UpdateInstance; + TSurfaceQueryFunction QueryInstanceRenderSurface; + TNeedsRenderFunction NeedsRenderFunction; + TRenderFunction RenderInstance; + TPickFunction Pick; + TReleaseInstanceFunction ReleaseInstance; + +} TRenderPluginClass; + +// We look for this function name in the shared library +#define QT3DS_RENDER_PLUGIN_CREATE_CLASS_FUNCION_NAME "CreateRenderPlugin" + +/* + * Function signature we expect mapped to "CreateRenderPlugin". Example code: + * + extern "C" { + +#ifdef _WIN32 +#define PLUGIN_EXPORT_API __declspec(dllexport) +#else +#define PLUGIN_EXPORT_API +#endif + + + +PLUGIN_EXPORT_API TRenderPluginClass CreateRenderPlugin( const char*) +{ + GearClass* classItem = (GearClass*)malloc( sizeof(GearClass) ); + TRenderPluginClass retval; + memset( &retval, 0, sizeof( TRenderPluginClass ) ); + retval.m_Class = classItem; + retval.GetRenderPluginAPIVersion = GetAPIVersion; + retval.CreateInstance = CreateInstance; + retval.CreateInstanceScriptProxy = CreateInstanceScriptProxy; + retval.UpdateInstance = UpdateInstance; + retval.QueryInstanceRenderSurface = QuerySurface; + retval.RenderInstance = Render; + retval.ReleaseInstance = ReleaseInstance; + retval.ReleaseClass = ReleaseClass; + return retval; +} + + * Required API function. + */ + +typedef TRenderPluginClass (*TCreateRenderPluginClassFunction)(const char *inTypeName); + +#ifdef _cplusplus +} +#endif + +#endif diff --git a/src/runtimerender/Qt3DSRenderPluginGraphObject.h b/src/runtimerender/Qt3DSRenderPluginGraphObject.h new file mode 100644 index 0000000..c0ea66b --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPluginGraphObject.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PLUGIN_GRAPH_OBJECT_H +#define QT3DS_RENDER_PLUGIN_GRAPH_OBJECT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +namespace qt3ds { +namespace render { + + struct SRenderPlugin : public SGraphObject + { + CRegisteredString m_PluginPath; + NodeFlags m_Flags; + + SRenderPlugin() + : SGraphObject(GraphObjectTypes::RenderPlugin) + { + } + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_PluginPath); + } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderPluginPropertyValue.h b/src/runtimerender/Qt3DSRenderPluginPropertyValue.h new file mode 100644 index 0000000..a266e8d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderPluginPropertyValue.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PLUGIN_PROPERTY_VALUE_H +#define QT3DS_RENDER_PLUGIN_PROPERTY_VALUE_H +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSDiscriminatedUnion.h" +#include "Qt3DSRenderPlugin.h" + +namespace qt3ds { +namespace foundation { + + template <> + struct DestructTraits<CRegisteredString> + { + void destruct(CRegisteredString &) {} + }; +} +} + +namespace qt3ds { +namespace render { + + template <typename TDatatype> + struct SRenderPluginPropertyValueTypeMap + { + }; + + template <> + struct SRenderPluginPropertyValueTypeMap<QT3DSI32> + { + enum { TypeMap = RenderPluginPropertyValueTypes::Long }; + }; + template <> + struct SRenderPluginPropertyValueTypeMap<QT3DSF32> + { + enum { TypeMap = RenderPluginPropertyValueTypes::Float }; + }; + template <> + struct SRenderPluginPropertyValueTypeMap<CRegisteredString> + { + enum { TypeMap = RenderPluginPropertyValueTypes::String }; + }; + template <> + struct SRenderPluginPropertyValueTypeMap<bool> + { + enum { TypeMap = RenderPluginPropertyValueTypes::Boolean }; + }; + + struct SRenderPluginPropertyValueUnionTraits + { + typedef RenderPluginPropertyValueTypes::Enum TIdType; + enum { + TBufferSize = sizeof(CRegisteredString), + }; + + static TIdType getNoDataId() + { + return RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue; + } + + template <typename TDataType> + static TIdType getType() + { + return (TIdType)SRenderPluginPropertyValueTypeMap<TDataType>::TypeMap; + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case RenderPluginPropertyValueTypes::String: + return inVisitor(*NVUnionCast<CRegisteredString *>(inData)); + case RenderPluginPropertyValueTypes::Float: + return inVisitor(*NVUnionCast<QT3DSF32 *>(inData)); + case RenderPluginPropertyValueTypes::Long: + return inVisitor(*NVUnionCast<QT3DSI32 *>(inData)); + default: + QT3DS_ASSERT(false); + case RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue: + return inVisitor(); + } + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case RenderPluginPropertyValueTypes::String: + return inVisitor(*NVUnionCast<const CRegisteredString *>(inData)); + case RenderPluginPropertyValueTypes::Float: + return inVisitor(*NVUnionCast<const QT3DSF32 *>(inData)); + case RenderPluginPropertyValueTypes::Long: + return inVisitor(*NVUnionCast<const QT3DSI32 *>(inData)); + default: + QT3DS_ASSERT(false); + case RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue: + return inVisitor(); + } + } + }; + + typedef qt3ds::foundation:: + DiscriminatedUnion<qt3ds::foundation:: + DiscriminatedUnionGenericBase<SRenderPluginPropertyValueUnionTraits, + SRenderPluginPropertyValueUnionTraits:: + TBufferSize>, + SRenderPluginPropertyValueUnionTraits::TBufferSize> + TRenderPluginPropertyValueUnionType; + + struct SRenderPluginPropertyValue : public TRenderPluginPropertyValueUnionType + { + typedef TRenderPluginPropertyValueUnionType TBase; + SRenderPluginPropertyValue() {} + SRenderPluginPropertyValue(const CRegisteredString &str) + : TBase(str) + { + } + SRenderPluginPropertyValue(QT3DSI32 value) + : TBase(value) + { + } + SRenderPluginPropertyValue(QT3DSF32 value) + : TBase(value) + { + } + SRenderPluginPropertyValue(const SRenderPluginPropertyValue &other) + : TBase(static_cast<const TBase &>(other)) + { + } + SRenderPluginPropertyValue &operator=(const SRenderPluginPropertyValue &other) + { + TBase::operator=(other); + return *this; + } + }; + + struct SRenderPropertyValueUpdate + { + // Should be the componentized name, so colors get .r .g .b appended + // and vectors get .x .y etc. This interface only updates a component at a time. + CRegisteredString m_PropertyName; + SRenderPluginPropertyValue m_Value; + SRenderPropertyValueUpdate() {} + SRenderPropertyValueUpdate(CRegisteredString str, const SRenderPluginPropertyValue &v) + : m_PropertyName(str) + , m_Value(v) + { + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderProfiler.h b/src/runtimerender/Qt3DSRenderProfiler.h new file mode 100644 index 0000000..a5941c0 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderProfiler.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PROFILER_H +#define QT3DS_RENDER_PROFILER_H +#include "Qt3DSRender.h" +#include "EASTL/string.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/Qt3DSContainers.h" + +namespace qt3ds { +namespace render { + + /** + * Opaque profiling system for rendering. + */ + class IRenderProfiler : public NVRefCounted + { + public: + typedef nvvector<CRegisteredString> TStrIDVec; + + protected: + virtual ~IRenderProfiler() {} + + public: + /** + * @brief start a timer query + * + * @param[in] nameID Timer ID for tracking + * @param[in] absoluteTime If true the absolute GPU is queried + * @param[in] sync Do a sync before starting the timer + * + * @return no return + */ + virtual void StartTimer(CRegisteredString &nameID, bool absoluteTime, bool sync) = 0; + + /** + * @brief stop a timer query + * + * @param[in] nameID Timer ID for tracking + * + * @return no return + */ + virtual void EndTimer(CRegisteredString &nameID) = 0; + + /** + * @brief Get elapsed timer value. Not this is an averaged time over several frames + * + * @param[in] nameID Timer ID for tracking + * + * @return no return + */ + virtual QT3DSF64 GetElapsedTime(const CRegisteredString &nameID) const = 0; + + /** + * @brief Get ID list of tracked timers + * + * @return ID list + */ + virtual const TStrIDVec &GetTimerIDs() const = 0; + + /** + * @brief add vertex count to current counter + * + * @return + */ + virtual void AddVertexCount(QT3DSU32 count) = 0; + + /** + * @brief get current vertex count and reset + * + * @return + */ + virtual QT3DSU32 GetAndResetTriangleCount() const = 0; + + static IRenderProfiler &CreateGpuProfiler(NVFoundationBase &inFoundation, + IQt3DSRenderContext &inContext, + NVRenderContext &inRenderContext); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderRay.cpp b/src/runtimerender/Qt3DSRenderRay.cpp new file mode 100644 index 0000000..5376b8b --- /dev/null +++ b/src/runtimerender/Qt3DSRenderRay.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderRay.h" +#include "foundation/Qt3DSPlane.h" + +using namespace qt3ds::render; + +// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm + +Option<QT3DSVec3> SRay::Intersect(const NVPlane &inPlane) const +{ + QT3DSF32 Vd = inPlane.n.dot(m_Direction); + if (fabs(Vd) < .0001f) + return Empty(); + QT3DSF32 V0 = -1.0f * (inPlane.n.dot(m_Origin) + inPlane.d); + QT3DSF32 t = V0 / Vd; + return m_Origin + (m_Direction * t); +} + +Option<SRayIntersectionResult> SRay::IntersectWithAABB(const QT3DSMat44 &inGlobalTransform, + const NVBounds3 &inBounds, + bool inForceIntersect) const +{ + // Intersect the origin with the AABB described by bounds. + + // Scan each axis separately. This code basically finds the distance + // distance from the origin to the near and far bbox planes for a given + // axis. It then divides this distance by the direction for that axis to + // get a range of t [near,far] that the ray intersects assuming the ray is + // described via origin + t*(direction). Running through all three axis means + // that you need to min/max those ranges together to find a global min/max + // that the pick could possibly be in. + + // Transform pick origin and direction into the subset's space. + QT3DSMat44 theOriginTransform = inGlobalTransform.getInverse(); + + QT3DSVec3 theTransformedOrigin = theOriginTransform.transform(m_Origin); + QT3DSF32 *outOriginTransformPtr(theOriginTransform.front()); + outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f; + QT3DSVec3 theTransformedDirection = theOriginTransform.rotate(m_Direction); + + static const QT3DSF32 KD_FLT_MAX = 3.40282346638528860e+38; + static const QT3DSF32 kEpsilon = 1e-5f; + + QT3DSF32 theMinWinner = -KD_FLT_MAX; + QT3DSF32 theMaxWinner = KD_FLT_MAX; + + for (QT3DSU32 theAxis = 0; theAxis < 3; ++theAxis) { + // Extract the ranges and direction for this axis + QT3DSF32 theMinBox = inBounds.minimum[theAxis]; + QT3DSF32 theMaxBox = inBounds.maximum[theAxis]; + QT3DSF32 theDirectionAxis = theTransformedDirection[theAxis]; + QT3DSF32 theOriginAxis = theTransformedOrigin[theAxis]; + + QT3DSF32 theMinAxis = -KD_FLT_MAX; + QT3DSF32 theMaxAxis = KD_FLT_MAX; + if (theDirectionAxis > kEpsilon) { + theMinAxis = (theMinBox - theOriginAxis) / theDirectionAxis; + theMaxAxis = (theMaxBox - theOriginAxis) / theDirectionAxis; + } else if (theDirectionAxis < -kEpsilon) { + theMinAxis = (theMaxBox - theOriginAxis) / theDirectionAxis; + theMaxAxis = (theMinBox - theOriginAxis) / theDirectionAxis; + } else if ((theOriginAxis < theMinBox || theOriginAxis > theMaxBox) + && inForceIntersect == false) { + // Pickray is roughly parallel to the plane of the slab + // so, if the origin is not in the range, we have no intersection + return Empty(); + } + + // Shrink the intersections to find the closest hit + theMinWinner = NVMax(theMinWinner, theMinAxis); + theMaxWinner = NVMin(theMaxWinner, theMaxAxis); + + if ((theMinWinner > theMaxWinner || theMaxWinner < 0) && inForceIntersect == false) + return Empty(); + } + + QT3DSVec3 scaledDir = theTransformedDirection * theMinWinner; + QT3DSVec3 newPosInLocal = theTransformedOrigin + scaledDir; + QT3DSVec3 newPosInGlobal = inGlobalTransform.transform(newPosInLocal); + QT3DSVec3 cameraToLocal = m_Origin - newPosInGlobal; + + QT3DSF32 rayLengthSquared = cameraToLocal.magnitudeSquared(); + + QT3DSF32 xRange = inBounds.maximum.x - inBounds.minimum.x; + QT3DSF32 yRange = inBounds.maximum.y - inBounds.minimum.y; + + QT3DSVec2 relXY; + relXY.x = (newPosInLocal[0] - inBounds.minimum.x) / xRange; + relXY.y = (newPosInLocal[1] - inBounds.minimum.y) / yRange; + + return SRayIntersectionResult(rayLengthSquared, relXY); +} + +Option<QT3DSVec2> SRay::GetRelative(const QT3DSMat44 &inGlobalTransform, const NVBounds3 &inBounds, + SBasisPlanes::Enum inPlane) const +{ + QT3DSMat44 theOriginTransform = inGlobalTransform.getInverse(); + + QT3DSVec3 theTransformedOrigin = theOriginTransform.transform(m_Origin); + QT3DSF32 *outOriginTransformPtr(theOriginTransform.front()); + outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f; + QT3DSVec3 theTransformedDirection = theOriginTransform.rotate(m_Direction); + + // The XY plane is going to be a plane with either positive or negative Z direction that runs + // through + QT3DSVec3 theDirection(0, 0, 1); + QT3DSVec3 theRight(1, 0, 0); + QT3DSVec3 theUp(0, 1, 0); + switch (inPlane) { + case SBasisPlanes::XY: + break; + case SBasisPlanes::XZ: + theDirection = QT3DSVec3(0, 1, 0); + theUp = QT3DSVec3(0, 0, 1); + break; + case SBasisPlanes::YZ: + theDirection = QT3DSVec3(1, 0, 0); + theRight = QT3DSVec3(0, 0, 1); + break; + } + NVPlane thePlane(theDirection, theDirection.dot(theTransformedDirection) > 0.0f + ? theDirection.dot(inBounds.maximum) + : theDirection.dot(inBounds.minimum)); + + SRay relativeRay(theTransformedOrigin, theTransformedDirection); + Option<QT3DSVec3> localIsect = relativeRay.Intersect(thePlane); + if (localIsect.hasValue()) { + QT3DSF32 xRange = theRight.dot(inBounds.maximum) - theRight.dot(inBounds.minimum); + QT3DSF32 yRange = theUp.dot(inBounds.maximum) - theUp.dot(inBounds.minimum); + QT3DSF32 xOrigin = xRange / 2.0f + theRight.dot(inBounds.minimum); + QT3DSF32 yOrigin = yRange / 2.0f + theUp.dot(inBounds.minimum); + return QT3DSVec2((theRight.dot(*localIsect) - xOrigin) / xRange, + (theUp.dot(*localIsect) - yOrigin) / yRange); + } + return Empty(); +} diff --git a/src/runtimerender/Qt3DSRenderRay.h b/src/runtimerender/Qt3DSRenderRay.h new file mode 100644 index 0000000..705f708 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderRay.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_RAY_H +#define QT3DS_RENDER_RAY_H + +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DSOption.h" +#include "foundation/Qt3DSMat44.h" +#include "foundation/Qt3DSBounds3.h" + +namespace qt3ds { +namespace render { + + struct SBasisPlanes + { + enum Enum { + XY = 0, + YZ, + XZ, + }; + }; + + struct SRayIntersectionResult + { + QT3DSF32 m_RayLengthSquared; // Length of the ray in world coordinates for the hit. + QT3DSVec2 m_RelXY; // UV coords for further mouse picking against a offscreen-rendered object. + SRayIntersectionResult() + : m_RayLengthSquared(0) + , m_RelXY(0, 0) + { + } + SRayIntersectionResult(QT3DSF32 rl, QT3DSVec2 relxy) + : m_RayLengthSquared(rl) + , m_RelXY(relxy) + { + } + }; + + struct SRay + { + QT3DSVec3 m_Origin; + QT3DSVec3 m_Direction; + SRay() + : m_Origin(0, 0, 0) + , m_Direction(0, 0, 0) + { + } + SRay(const QT3DSVec3 &inOrigin, const QT3DSVec3 &inDirection) + : m_Origin(inOrigin) + , m_Direction(inDirection) + { + } + // If we are parallel, then no intersection of course. + Option<QT3DSVec3> Intersect(const NVPlane &inPlane) const; + + Option<SRayIntersectionResult> IntersectWithAABB(const QT3DSMat44 &inGlobalTransform, + const NVBounds3 &inBounds, + bool inForceIntersect = false) const; + + Option<QT3DSVec2> GetRelative(const QT3DSMat44 &inGlobalTransform, const NVBounds3 &inBounds, + SBasisPlanes::Enum inPlane) const; + + Option<QT3DSVec2> GetRelativeXY(const QT3DSMat44 &inGlobalTransform, + const NVBounds3 &inBounds) const + { + return GetRelative(inGlobalTransform, inBounds, SBasisPlanes::XY); + } + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderRenderList.cpp b/src/runtimerender/Qt3DSRenderRenderList.cpp new file mode 100644 index 0000000..05a7b2c --- /dev/null +++ b/src/runtimerender/Qt3DSRenderRenderList.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderRenderList.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSContainers.h" +#include "render/Qt3DSRenderBaseTypes.h" + +using namespace qt3ds::render; + +namespace { + +struct SRenderList : public IRenderList +{ + typedef eastl::pair<QT3DSU32, IRenderTask *> TTaskIdTaskPair; + typedef nvvector<TTaskIdTaskPair> TTaskList; + + NVFoundationBase &m_Foundation; + TTaskList m_Tasks; + TTaskList m_PersistentTasks; + QT3DSU32 m_NextTaskId; + QT3DSI32 mRefCount; + bool m_ScissorEnabled; + NVRenderRect m_ScissorRect; + NVRenderRect m_Viewport; + + SRenderList(NVFoundationBase &fnd) + : m_Foundation(fnd) + , m_Tasks(fnd.getAllocator(), "m_Tasks") + , m_PersistentTasks(fnd.getAllocator(), "m_PersistentTasks") + , m_NextTaskId(1) + , mRefCount(0) + , m_ScissorEnabled(false) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void BeginFrame() override + { + m_NextTaskId = 1; + m_Tasks.clear(); + } + + QT3DSU32 AddRenderTask(IRenderTask &inTask) override + { + QT3DSU32 taskId = m_NextTaskId; + if (inTask.persistent()) { + m_PersistentTasks.push_back(eastl::make_pair(0, &inTask)); + taskId = 0; + } else { + QT3DSU32 taskId = m_NextTaskId; + ++m_NextTaskId; + m_Tasks.push_back(eastl::make_pair(taskId, &inTask)); + } + return taskId; + } + + void DiscardRenderTask(QT3DSU32 inTaskId) override + { + TTaskList::iterator iter, end; + for (iter = m_Tasks.begin(), end = m_Tasks.end(); iter != end && iter->first != inTaskId; + ++iter) { + } + if (iter != end) + m_Tasks.erase(iter); + } + // This runs through the added tasks in reverse order. This is used to render dependencies + // before rendering to the main render target. + void RunRenderTasks() override + { + for (TTaskList::reverse_iterator iter = m_PersistentTasks.rbegin(), + end = m_PersistentTasks.rend(); iter != end; + ++iter) { + iter->second->Run(); + } + for (TTaskList::reverse_iterator iter = m_Tasks.rbegin(), end = m_Tasks.rend(); iter != end; + ++iter) { + iter->second->Run(); + } + BeginFrame(); + } + + void SetScissorTestEnabled(bool enabled) override { m_ScissorEnabled = enabled; } + void SetScissorRect(NVRenderRect rect) override { m_ScissorRect = rect; } + void SetViewport(NVRenderRect rect) override { m_Viewport = rect; } + bool IsScissorTestEnabled() const override { return m_ScissorEnabled; } + NVRenderRect GetScissor() const override { return m_ScissorRect; } + NVRenderRect GetViewport() const override { return m_Viewport; } +}; +} + +IRenderList &IRenderList::CreateRenderList(NVFoundationBase &fnd) +{ + return *QT3DS_NEW(fnd.getAllocator(), SRenderList)(fnd); +} diff --git a/src/runtimerender/Qt3DSRenderRenderList.h b/src/runtimerender/Qt3DSRenderRenderList.h new file mode 100644 index 0000000..957c3d7 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderRenderList.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_RENDER_LIST_H +#define QT3DS_RENDER_RENDER_LIST_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" + +namespace qt3ds { +namespace render { + + class IRenderTask + { + public: + virtual ~IRenderTask() {} + virtual void Run() = 0; + virtual bool persistent() const + { + return false; + } + }; + + /** + * The render list exists so that dependencies of the main render target can render themselves + * completely before the main render target renders. From Maxwell GPU's on, we have a tiled + * architecture. This tiling mechanism is sensitive to switching the render target, so we would + * really like to render completely to a single render target and then switch. With our layered + * render architecture, this is not very feasible unless dependencies render themselves before + * the + * main set of layers render themselves. Furthermore it benefits the overall software + * architecture + * to have a distinct split between the prepare for render step and the render step and using + * this + * render list allows us to avoid some level of repeated tree traversal at the cost of some + * minimal + * per frame allocation. The rules for using the render list are that you need to add yourself + * before + * your dependencies do; the list is iterated in reverse during RunRenderTasks. So a layer adds + * itself + * (if it is going to render offscreen) before it runs through its renderable list to prepare + * each object + * because it is during the renderable prepare traversale that subpresentations will get added + * by + * the offscreen render manager. + */ + class IRenderList : public NVRefCounted + { + public: + // Called by the render context, do not call this. + virtual void BeginFrame() = 0; + + // Next tell all sub render target rendering systems to add themselves to the render list. + // At this point + // we agree to *not* have rendered anything, no clears or anything so if you are caching + // render state and you detect nothing has changed it may not be necessary to swap egl + // buffers. + virtual QT3DSU32 AddRenderTask(IRenderTask &inTask) = 0; + virtual void DiscardRenderTask(QT3DSU32 inTaskId) = 0; + // This runs through the added tasks in reverse order. This is used to render dependencies + // before rendering to the main render target. + virtual void RunRenderTasks() = 0; + + // We used to use GL state to pass information down the callstack. + // I have replaced those calls with this state here because that information + // controls how layers size themselves (which is quite a complicated process). + virtual void SetScissorTestEnabled(bool enabled) = 0; + virtual void SetScissorRect(NVRenderRect rect) = 0; + virtual void SetViewport(NVRenderRect rect) = 0; + virtual bool IsScissorTestEnabled() const = 0; + virtual NVRenderRect GetScissor() const = 0; + virtual NVRenderRect GetViewport() const = 0; + + static IRenderList &CreateRenderList(NVFoundationBase &inFnd); + }; + + // Now for scoped property access. + template <typename TDataType> + struct SRenderListScopedProperty + : public qt3ds::render::NVRenderGenericScopedProperty<IRenderList, TDataType> + { + typedef qt3ds::render::NVRenderGenericScopedProperty<IRenderList, TDataType> TBaseType; + typedef typename TBaseType::TGetter TGetter; + typedef typename TBaseType::TSetter TSetter; + SRenderListScopedProperty(IRenderList &ctx, TGetter getter, TSetter setter) + : TBaseType(ctx, getter, setter) + { + } + SRenderListScopedProperty(IRenderList &ctx, TGetter getter, TSetter setter, + const TDataType &inNewValue) + : TBaseType(ctx, getter, setter, inNewValue) + { + } + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderRotationHelper.h b/src/runtimerender/Qt3DSRenderRotationHelper.h new file mode 100644 index 0000000..19ba8eb --- /dev/null +++ b/src/runtimerender/Qt3DSRenderRotationHelper.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_ROTATION_HELPER_H +#define QT3DS_RENDER_ROTATION_HELPER_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderNode.h" +#include "EASTL/utility.h" + +namespace qt3ds { +namespace render { + /** + * Unfortunately we still use an XYZ-Euler rotation system. This means that identical + *rotations + * can be represented in various ways. We need to ensure that the value that we write in the + * inspector palette are reasonable, however, and to do this we need a least-distance function + * from two different xyz tuples. + */ + + struct SRotationHelper + { + + // Attempt to go for negative values intead of large positive ones + // Goal is to keep the fabs of the angle low. + static QT3DSF32 ToMinimalAngle(QT3DSF32 value) + { + QT3DSF32 epsilon = (QT3DSF32)M_PI + .001f; + while (fabs(value) > epsilon) { + QT3DSF32 tpi = (QT3DSF32)(2.0f * M_PI); + if (value > 0.0f) + value -= tpi; + else + value += tpi; + } + return value; + } + + /** + * Convert an angle to a canonical form. Return this canonical form. + * + * The canonical form is defined as: + * 1. XYZ all positive. + * 2. XYZ all less than 360. + * + * To do this we rely on two identities, the first is that given an angle, adding + * (or subtracting) pi from all three components does not change the angle. + * + * The second is the obvious one that adding or subtracting 2*pi from any single + * component does not change the angle. + * + * Note that this function works in radian space. + */ + static QT3DSVec3 ToCanonicalFormStaticAxis(const QT3DSVec3 &inSrcAngle, QT3DSU32 inRotOrder) + { + // step 1 - reduce all components to less than 2*pi but greater than 0 + QT3DSVec3 retval(inSrcAngle); + retval.x = ToMinimalAngle(retval.x); + retval.y = ToMinimalAngle(retval.y); + retval.z = ToMinimalAngle(retval.z); + + // step 2 - if any two components are equal to or greater than pi + // then subtract pi from all three, then run two pi reduce again. + + QT3DSU32 greaterThanPiSum = 0; + QT3DSF32 pi = (QT3DSF32)M_PI; + for (QT3DSU32 idx = 0; idx < 3; ++idx) + if (fabs(retval[idx]) >= pi) + greaterThanPiSum++; + + if (greaterThanPiSum > 1) { + // For this identity to work, the middle axis angle needs to be subtracted from + // 180 instead of added to 180 because the previous axis *reversed* it. + QT3DSU32 theMiddleAxis = 0; + + switch (inRotOrder) { + case EulOrdXYZs: + theMiddleAxis = 1; + break; + case EulOrdXZYs: + theMiddleAxis = 2; + break; + case EulOrdYXZs: + theMiddleAxis = 0; + break; + case EulOrdYZXs: + theMiddleAxis = 2; + break; + case EulOrdZYXs: + theMiddleAxis = 1; + break; + case EulOrdZXYs: + theMiddleAxis = 0; + break; + case EulOrdXYZr: + theMiddleAxis = 1; + break; + case EulOrdXZYr: + theMiddleAxis = 2; + break; + case EulOrdYXZr: + theMiddleAxis = 0; + break; + case EulOrdYZXr: + theMiddleAxis = 2; + break; + case EulOrdZYXr: + theMiddleAxis = 1; + break; + case EulOrdZXYr: + theMiddleAxis = 0; + break; + default: + QT3DS_ASSERT(false); + return inSrcAngle; + } + for (QT3DSU32 idx = 0; idx < 3; ++idx) { + if (idx == theMiddleAxis) + retval[idx] = pi - retval[idx]; + else + retval[idx] = retval[idx] > 0.0f ? retval[idx] - pi : retval[idx] + pi; + } + } + return retval; + } + + static QT3DSVec3 ToMinimalAngleDiff(const QT3DSVec3 inDiff) + { + return QT3DSVec3(ToMinimalAngle(inDiff.x), ToMinimalAngle(inDiff.y), + ToMinimalAngle(inDiff.z)); + } + + /** + * Given an old angle and a new angle, return an angle has the same rotational value + * as the new angle *but* is as close to the old angle as possible. + * Works in radian space. This function doesn't currently work for euler angles or + * with Euler angles with repeating axis. + */ + static QT3DSVec3 ToNearestAngle(const QT3DSVec3 &inOldAngle, const QT3DSVec3 &inNewAngle, + QT3DSU32 inRotOrder) + { + switch (inRotOrder) { + case EulOrdXYZs: + case EulOrdXZYs: + case EulOrdYXZs: + case EulOrdYZXs: + case EulOrdZYXs: + case EulOrdZXYs: + case EulOrdXYZr: + case EulOrdXZYr: + case EulOrdYXZr: + case EulOrdYZXr: + case EulOrdZYXr: + case EulOrdZXYr: { + QT3DSVec3 oldA = ToCanonicalFormStaticAxis(inOldAngle, inRotOrder); + QT3DSVec3 newA = ToCanonicalFormStaticAxis(inNewAngle, inRotOrder); + QT3DSVec3 diff = newA - oldA; + return inOldAngle + ToMinimalAngleDiff(diff); + } break; + default: + return inNewAngle; + } + } + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderShaderCache.cpp b/src/runtimerender/Qt3DSRenderShaderCache.cpp new file mode 100644 index 0000000..77d4ee6 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCache.cpp @@ -0,0 +1,770 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderShaderCache.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "StringTools.h" +#include "foundation/XML.h" +#include "foundation/IOStreams.h" +#include "foundation/StringConversionImpl.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "foundation/FileTools.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderer.h" +#include <memory> +#include "foundation/Qt3DSTime.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "EASTL/sort.h" + +#include <QRegularExpression> +#include <QString> + +using namespace qt3ds::render; + +namespace { +using qt3ds::render::NVRenderContextScopedProperty; +const char *TessellationEnabledStr = "TessellationStageEnabled"; +const char *GeometryEnabledStr = "GeometryStageEnabled"; +inline void AppendFlagValue(Qt3DSString &inStr, const char *flag) +{ + if (inStr.length()) + inStr.append(QLatin1Char(',')); + inStr.append(flag); +} +inline void CacheFlagsToStr(const SShaderCacheProgramFlags &inFlags, Qt3DSString &inString) +{ + inString.clear(); + if (inFlags.IsTessellationEnabled()) + AppendFlagValue(inString, TessellationEnabledStr); + if (inFlags.IsGeometryShaderEnabled()) + AppendFlagValue(inString, GeometryEnabledStr); +} + +struct ShaderType +{ + enum Enum { Vertex, TessControl, TessEval, Fragment, Geometry, Compute }; +}; + +inline ShaderType::Enum StringToShaderType(Qt3DSString &inShaderType) +{ + ShaderType::Enum retval = ShaderType::Vertex; + + if (inShaderType.size() == 0) + return retval; + + if (!inShaderType.compare("VertexCode")) + retval = ShaderType::Vertex; + else if (!inShaderType.compare("FragmentCode")) + retval = ShaderType::Fragment; + else if (!inShaderType.compare("TessControlCode")) + retval = ShaderType::TessControl; + else if (!inShaderType.compare("TessEvalCode")) + retval = ShaderType::TessEval; + else if (!inShaderType.compare("GeometryCode")) + retval = ShaderType::Geometry; + else + QT3DS_ASSERT(false); + + return retval; +} + +inline SShaderCacheProgramFlags CacheFlagsToStr(const Qt3DSString &inString) +{ + SShaderCacheProgramFlags retval; + if (inString.indexOf(TessellationEnabledStr) != Qt3DSString::npos) + retval.SetTessellationEnabled(true); + if (inString.indexOf(GeometryEnabledStr) != Qt3DSString::npos) + retval.SetGeometryShaderEnabled(true); + return retval; +} + +typedef eastl::pair<const char *, NVRenderContextValues::Enum> TStringToContextValuePair; + +/*GLES2 = 1 << 0, +GL2 = 1 << 1, +GLES3 = 1 << 2, +GL3 = 1 << 3, +GL4 = 1 << 4, +NullContext = 1 << 5,*/ +TStringToContextValuePair g_StringToContextTypeValue[] = { + TStringToContextValuePair("GLES2", NVRenderContextValues::GLES2), + TStringToContextValuePair("GL2", NVRenderContextValues::GL2), + TStringToContextValuePair("GLES3", NVRenderContextValues::GLES3), + TStringToContextValuePair("GLES3PLUS", NVRenderContextValues::GLES3PLUS), + TStringToContextValuePair("GL3", NVRenderContextValues::GL3), + TStringToContextValuePair("GL4", NVRenderContextValues::GL4), + TStringToContextValuePair("NullContext", NVRenderContextValues::NullContext), +}; + +size_t g_NumStringToContextValueEntries = + sizeof(g_StringToContextTypeValue) / sizeof(*g_StringToContextTypeValue); + +inline void ContextTypeToString(qt3ds::render::NVRenderContextType inType, + Qt3DSString &outContextType) +{ + outContextType.clear(); + for (size_t idx = 0, end = g_NumStringToContextValueEntries; idx < end; ++idx) { + if (inType & g_StringToContextTypeValue[idx].second) { + if (outContextType.size()) + outContextType.append('|'); + outContextType.append(g_StringToContextTypeValue[idx].first); + } + } +} + +inline qt3ds::render::NVRenderContextType StringToContextType(const Qt3DSString &inContextType) +{ + qt3ds::render::NVRenderContextType retval; + char tempBuffer[128]; + memZero(tempBuffer, 128); + const QString::size_type lastTempBufIdx = 127; + QString::size_type pos = 0, lastpos = 0; + if (inContextType.size() == 0) + return retval; + + do { + pos = int(inContextType.indexOf(QLatin1Char('|'), lastpos)); + if (pos == Qt3DSString::npos) + pos = int(inContextType.size()); + { + + QString::size_type sectionLen = NVMin(pos - lastpos, lastTempBufIdx); + qt3ds::intrinsics::memCopy(tempBuffer, inContextType.toUtf8().constData() + lastpos, + sectionLen); + tempBuffer[lastTempBufIdx] = 0; + for (size_t idx = 0, end = g_NumStringToContextValueEntries; idx < end; ++idx) { + if (strcmp(g_StringToContextTypeValue[idx].first, tempBuffer) == 0) + retval = retval | g_StringToContextTypeValue[idx].second; + } + } + // iterate past the bar + ++pos; + lastpos = pos; + } while (pos < inContextType.size() && pos != Qt3DSString::npos); + + return retval; +} + +struct SShaderCacheKey +{ + CRegisteredString m_Key; + eastl::vector<SShaderPreprocessorFeature> m_Features; + size_t m_HashCode; + + SShaderCacheKey(CRegisteredString key = CRegisteredString()) + : m_Key(key) + , m_HashCode(0) + { + } + + SShaderCacheKey(const SShaderCacheKey &other) + : m_Key(other.m_Key) + , m_Features(other.m_Features) + , m_HashCode(other.m_HashCode) + { + } + + SShaderCacheKey &operator=(const SShaderCacheKey &other) + { + m_Key = other.m_Key; + m_Features = other.m_Features; + m_HashCode = other.m_HashCode; + return *this; + } + + void GenerateHashCode() + { + m_HashCode = m_Key.hash(); + m_HashCode = m_HashCode + ^ HashShaderFeatureSet(toDataRef(m_Features.data(), (QT3DSU32)m_Features.size())); + } + bool operator==(const SShaderCacheKey &inOther) const + { + return m_Key == inOther.m_Key && m_Features == inOther.m_Features; + } +}; +} + +namespace eastl { +template <> +struct hash<SShaderCacheKey> +{ + size_t operator()(const SShaderCacheKey &inKey) const { return inKey.m_HashCode; } +}; +} + +namespace { + +struct ShaderCache : public IShaderCache +{ + typedef nvhash_map<SShaderCacheKey, NVScopedRefCounted<NVRenderShaderProgram>> TShaderMap; + NVRenderContext &m_RenderContext; + IPerfTimer &m_PerfTimer; + TShaderMap m_Shaders; + Qt3DSString m_CacheFilePath; + Qt3DSString m_VertexCode; + Qt3DSString m_TessCtrlCode; + Qt3DSString m_TessEvalCode; + Qt3DSString m_GeometryCode; + Qt3DSString m_FragmentCode; + Qt3DSString m_InsertStr; + Qt3DSString m_FlagString; + Qt3DSString m_ContextTypeString; + SShaderCacheKey m_TempKey; + + NVScopedRefCounted<IDOMWriter> m_ShaderCache; + IInputStreamFactory &m_InputStreamFactory; + bool m_ShaderCompilationEnabled; + volatile QT3DSI32 mRefCount; + + ShaderCache(NVRenderContext &ctx, IInputStreamFactory &inInputStreamFactory, + IPerfTimer &inPerfTimer) + : m_RenderContext(ctx) + , m_PerfTimer(inPerfTimer) + , m_Shaders(ctx.GetAllocator(), "ShaderCache::m_Shaders") + , m_InputStreamFactory(inInputStreamFactory) + , m_ShaderCompilationEnabled(true) + , mRefCount(0) + { + } + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) + + NVRenderShaderProgram *GetProgram(CRegisteredString inKey, + NVConstDataRef<SShaderPreprocessorFeature> inFeatures) override + { + m_TempKey.m_Key = inKey; + m_TempKey.m_Features.assign(inFeatures.begin(), inFeatures.end()); + m_TempKey.GenerateHashCode(); + TShaderMap::iterator theIter = m_Shaders.find(m_TempKey); + if (theIter != m_Shaders.end()) + return theIter->second; + return NULL; + } + + void AddBackwardCompatibilityDefines(ShaderType::Enum shaderType) + { + if (shaderType == ShaderType::Vertex || shaderType == ShaderType::TessControl + || shaderType == ShaderType::TessEval || shaderType == ShaderType::Geometry) { + m_InsertStr += "#define attribute in\n"; + m_InsertStr += "#define varying out\n"; + } else if (shaderType == ShaderType::Fragment) { + m_InsertStr += "#define varying in\n"; + m_InsertStr += "#define texture2D texture\n"; + m_InsertStr += "#define gl_FragColor fragOutput\n"; + + if (m_RenderContext.IsAdvancedBlendHwSupportedKHR()) + m_InsertStr += "layout(blend_support_all_equations) out;\n"; + + m_InsertStr += "#ifndef NO_FRAG_OUTPUT\n"; + m_InsertStr += "out vec4 fragOutput;\n"; + m_InsertStr += "#endif\n"; + } + } + + void AddShaderExtensionStrings(ShaderType::Enum shaderType, bool isGLES) + { + if (isGLES) { + if (m_RenderContext.IsStandardDerivativesSupported()) + m_InsertStr += "#extension GL_OES_standard_derivatives : enable\n"; + else + m_InsertStr += "#extension GL_OES_standard_derivatives : disable\n"; + } + + if (IQt3DSRenderer::IsGlEs3Context(m_RenderContext.GetRenderContextType())) { + if (shaderType == ShaderType::TessControl || shaderType == ShaderType::TessEval) { + m_InsertStr += "#extension GL_EXT_tessellation_shader : enable\n"; + } else if (shaderType == ShaderType::Geometry) { + m_InsertStr += "#extension GL_EXT_geometry_shader : enable\n"; + } else if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment) { + if (m_RenderContext.GetRenderBackendCap(render::NVRenderBackend::NVRenderBackendCaps::gpuShader5)) + m_InsertStr += "#extension GL_EXT_gpu_shader5 : enable\n"; + if (m_RenderContext.IsAdvancedBlendHwSupportedKHR()) + m_InsertStr += "#extension GL_KHR_blend_equation_advanced : enable\n"; + } + } else { + if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment + || shaderType == ShaderType::Geometry) { + if (m_RenderContext.GetRenderContextType() != NVRenderContextValues::GLES2) { + m_InsertStr += "#extension GL_ARB_gpu_shader5 : enable\n"; + m_InsertStr += "#extension GL_ARB_shading_language_420pack : enable\n"; + } + if (isGLES && m_RenderContext.IsTextureLodSupported()) + m_InsertStr += "#extension GL_EXT_shader_texture_lod : enable\n"; + if (m_RenderContext.IsShaderImageLoadStoreSupported()) + m_InsertStr += "#extension GL_ARB_shader_image_load_store : enable\n"; + if (m_RenderContext.IsAtomicCounterBufferSupported()) + m_InsertStr += "#extension GL_ARB_shader_atomic_counters : enable\n"; + if (m_RenderContext.IsStorageBufferSupported()) + m_InsertStr += "#extension GL_ARB_shader_storage_buffer_object : enable\n"; + if (m_RenderContext.IsAdvancedBlendHwSupportedKHR()) + m_InsertStr += "#extension GL_KHR_blend_equation_advanced : enable\n"; + } + } + } + + void AddShaderPreprocessor(Qt3DSString &str, CRegisteredString inKey, + ShaderType::Enum shaderType, + NVConstDataRef<SShaderPreprocessorFeature> inFeatures) + { + // Don't use shading language version returned by the driver as it might + // differ from the context version. Instead use the context type to specify + // the version string. + bool isGlES = IQt3DSRenderer::IsGlEsContext(m_RenderContext.GetRenderContextType()); + m_InsertStr.clear(); + int minor = m_RenderContext.format().minorVersion(); + QString versionStr; + QTextStream stream(&versionStr); + stream << "#version "; + const QT3DSU32 type = (QT3DSU32)m_RenderContext.GetRenderContextType(); + switch (type) { + case NVRenderContextValues::GLES2: + stream << "1" << minor << "0\n"; + break; + case NVRenderContextValues::GL2: + stream << "1" << minor << "0\n"; + break; + case NVRenderContextValues::GLES3PLUS: + case NVRenderContextValues::GLES3: + stream << "3" << minor << "0 es\n"; + break; + case NVRenderContextValues::GL3: + if (minor == 3) + stream << "3" << minor << "0\n"; + else + stream << "1" << 3 + minor << "0\n"; + break; + case NVRenderContextValues::GL4: + stream << "4" << minor << "0\n"; + break; + default: + QT3DS_ASSERT(false); + break; + } + + m_InsertStr.append(versionStr.toLatin1().data()); + + if (inFeatures.size()) { + for (QT3DSU32 idx = 0, end = inFeatures.size(); idx < end; ++idx) { + SShaderPreprocessorFeature feature(inFeatures[idx]); + m_InsertStr.append("#define "); + m_InsertStr.append(inFeatures[idx].m_Name.c_str()); + m_InsertStr.append(" "); + m_InsertStr.append(feature.m_Enabled ? "1" : "0"); + m_InsertStr.append("\n"); + } + } + + if (isGlES) { + if (!IQt3DSRenderer::IsGlEs3Context(m_RenderContext.GetRenderContextType())) { + if (shaderType == ShaderType::Fragment) { + m_InsertStr += "#define fragOutput gl_FragData[0]\n"; + } + } else { + m_InsertStr += "#define texture2D texture\n"; + } + + // add extenions strings before any other non-processor token + AddShaderExtensionStrings(shaderType, isGlES); + + // add precision qualifier depending on backend + if (IQt3DSRenderer::IsGlEs3Context(m_RenderContext.GetRenderContextType())) { + m_InsertStr.append("precision highp float;\n" + "precision highp int;\n"); + if( m_RenderContext.GetRenderBackendCap(render::NVRenderBackend::NVRenderBackendCaps::gpuShader5) ) { + m_InsertStr.append("precision mediump sampler2D;\n" + "precision mediump sampler2DArray;\n" + "precision mediump sampler2DShadow;\n"); + if (m_RenderContext.IsShaderImageLoadStoreSupported()) { + m_InsertStr.append("precision mediump image2D;\n"); + } + } + + AddBackwardCompatibilityDefines(shaderType); + } else { + // GLES2 + m_InsertStr.append("precision mediump float;\n" + "precision mediump int;\n" + "#define texture texture2D\n"); + if (m_RenderContext.IsTextureLodSupported()) + m_InsertStr.append("#define textureLod texture2DLodEXT\n"); + else + m_InsertStr.append("#define textureLod(s, co, lod) texture2D(s, co)\n"); + } + } else { + if (!IQt3DSRenderer::IsGl2Context(m_RenderContext.GetRenderContextType())) { + m_InsertStr += "#define texture2D texture\n"; + + AddShaderExtensionStrings(shaderType, isGlES); + + m_InsertStr += "#if __VERSION__ >= 330\n"; + + AddBackwardCompatibilityDefines(shaderType); + + m_InsertStr += "#else\n"; + if (shaderType == ShaderType::Fragment) { + m_InsertStr += "#define fragOutput gl_FragData[0]\n"; + } + m_InsertStr += "#endif\n"; + } + } + + if (inKey.IsValid()) { + m_InsertStr += "//Shader name -"; + m_InsertStr += inKey.c_str(); + m_InsertStr += "\n"; + } + + if (shaderType == ShaderType::TessControl) { + m_InsertStr += "#define TESSELLATION_CONTROL_SHADER 1\n"; + m_InsertStr += "#define TESSELLATION_EVALUATION_SHADER 0\n"; + } else if (shaderType == ShaderType::TessEval) { + m_InsertStr += "#define TESSELLATION_CONTROL_SHADER 0\n"; + m_InsertStr += "#define TESSELLATION_EVALUATION_SHADER 1\n"; + } + + str.insert(0, m_InsertStr); + } + // Compile this program overwriting any existing ones. + NVRenderShaderProgram * + ForceCompileProgram(CRegisteredString inKey, const char8_t *inVert, const char8_t *inFrag, + const char8_t *inTessCtrl, const char8_t *inTessEval, const char8_t *inGeom, + const SShaderCacheProgramFlags &inFlags, + NVConstDataRef<SShaderPreprocessorFeature> inFeatures, + bool separableProgram, bool fromDisk = false) override + { + if (m_ShaderCompilationEnabled == false) + return NULL; + SShaderCacheKey tempKey(inKey); + tempKey.m_Features.assign(inFeatures.begin(), inFeatures.end()); + tempKey.GenerateHashCode(); + + eastl::pair<TShaderMap::iterator, bool> theInserter = m_Shaders.insert(tempKey); + if (fromDisk) { + qCInfo(TRACE_INFO) << "Loading from persistent shader cache: '<" + << tempKey.m_Key << ">'"; + } else { + qCInfo(TRACE_INFO) << "Compiling into shader cache: '" + << tempKey.m_Key << ">'"; + } + + if (!inVert) + inVert = ""; + if (!inTessCtrl) + inTessCtrl = ""; + if (!inTessEval) + inTessEval = ""; + if (!inGeom) + inGeom = ""; + if (!inFrag) + inFrag = ""; + + SStackPerfTimer __perfTimer(m_PerfTimer, "Shader Compilation"); + m_VertexCode.assign(inVert); + m_TessCtrlCode.assign(inTessCtrl); + m_TessEvalCode.assign(inTessEval); + m_GeometryCode.assign(inGeom); + m_FragmentCode.assign(inFrag); + // Add defines and such so we can write unified shaders that work across platforms. + // vertex and fragment shaders are optional for separable shaders + if (!separableProgram || !m_VertexCode.isEmpty()) + AddShaderPreprocessor(m_VertexCode, inKey, ShaderType::Vertex, inFeatures); + if (!separableProgram || !m_FragmentCode.isEmpty()) + AddShaderPreprocessor(m_FragmentCode, inKey, ShaderType::Fragment, inFeatures); + // optional shaders + if (inFlags.IsTessellationEnabled()) { + QT3DS_ASSERT(m_TessCtrlCode.size() && m_TessEvalCode.size()); + AddShaderPreprocessor(m_TessCtrlCode, inKey, ShaderType::TessControl, inFeatures); + AddShaderPreprocessor(m_TessEvalCode, inKey, ShaderType::TessEval, inFeatures); + } + if (inFlags.IsGeometryShaderEnabled()) + AddShaderPreprocessor(m_GeometryCode, inKey, ShaderType::Geometry, inFeatures); + + theInserter.first->second = + m_RenderContext + .CompileSource(inKey, m_VertexCode.c_str(), QT3DSU32(m_VertexCode.size()), + m_FragmentCode.c_str(), QT3DSU32(m_FragmentCode.size()), + m_TessCtrlCode.c_str(), QT3DSU32(m_TessCtrlCode.size()), + m_TessEvalCode.c_str(), QT3DSU32(m_TessEvalCode.size()), + m_GeometryCode.c_str(), QT3DSU32(m_GeometryCode.size()), + separableProgram).mShader; + if (theInserter.first->second) { + if (m_ShaderCache) { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "Program"); + m_ShaderCache->Att("key", inKey.c_str()); + CacheFlagsToStr(inFlags, m_FlagString); + if (m_FlagString.size()) + m_ShaderCache->Att("glflags", m_FlagString.c_str()); + // write out the GL version. + { + qt3ds::render::NVRenderContextType theContextType = + m_RenderContext.GetRenderContextType(); + ContextTypeToString(theContextType, m_ContextTypeString); + m_ShaderCache->Att("gl-context-type", m_ContextTypeString.c_str()); + } + if (inFeatures.size()) { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "Features"); + for (QT3DSU32 idx = 0, end = inFeatures.size(); idx < end; ++idx) { + m_ShaderCache->Att(inFeatures[idx].m_Name, inFeatures[idx].m_Enabled); + } + } + + { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "VertexCode"); + m_ShaderCache->Value(inVert); + } + { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "FragmentCode"); + m_ShaderCache->Value(inFrag); + } + if (m_TessCtrlCode.size()) { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "TessControlCode"); + m_ShaderCache->Value(inTessCtrl); + } + if (m_TessEvalCode.size()) { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "TessEvalCode"); + m_ShaderCache->Value(inTessEval); + } + if (m_GeometryCode.size()) { + IDOMWriter::Scope __writeScope(*m_ShaderCache, "GeometryCode"); + m_ShaderCache->Value(inGeom); + } + } + } + return theInserter.first->second; + } + + virtual NVRenderShaderProgram * + CompileProgram(CRegisteredString inKey, const char8_t *inVert, const char8_t *inFrag, + const char8_t *inTessCtrl, const char8_t *inTessEval, const char8_t *inGeom, + const SShaderCacheProgramFlags &inFlags, + NVConstDataRef<SShaderPreprocessorFeature> inFeatures, bool separableProgram) override + { + NVRenderShaderProgram *theProgram = GetProgram(inKey, inFeatures); + if (theProgram) + return theProgram; + + NVRenderShaderProgram *retval = + ForceCompileProgram(inKey, inVert, inFrag, inTessCtrl, inTessEval, inGeom, inFlags, + inFeatures, separableProgram); + if (m_CacheFilePath.c_str() && m_ShaderCache && m_ShaderCompilationEnabled) { + CFileSeekableIOStream theStream(m_CacheFilePath.c_str(), FileWriteFlags()); + if (theStream.IsOpen()) { + NVScopedRefCounted<IStringTable> theStringTable( + IStringTable::CreateStringTable(m_RenderContext.GetAllocator())); + CDOMSerializer::WriteXMLHeader(theStream); + CDOMSerializer::Write(m_RenderContext.GetAllocator(), + *m_ShaderCache->GetTopElement(), theStream, *theStringTable); + } + } + return retval; + } + + void BootupDOMWriter() + { + NVScopedRefCounted<IStringTable> theStringTable( + IStringTable::CreateStringTable(m_RenderContext.GetAllocator())); + m_ShaderCache = IDOMWriter::CreateDOMWriter(m_RenderContext.GetAllocator(), + "Qt3DSShaderCache", theStringTable) + .first; + m_ShaderCache->Att("cache_version", IShaderCache::GetShaderVersion()); + } + + void SetShaderCachePersistenceEnabled(const char8_t *inDirectory) override + { + if (inDirectory == NULL) { + m_ShaderCache = NULL; + return; + } + BootupDOMWriter(); + m_CacheFilePath = QDir(inDirectory).filePath(GetShaderCacheFileName()); + + NVScopedRefCounted<IRefCountedInputStream> theInStream = + m_InputStreamFactory.GetStreamForFile(m_CacheFilePath.c_str()); + if (theInStream) { + SStackPerfTimer __perfTimer(m_PerfTimer, "ShaderCache - Load"); + NVScopedRefCounted<IStringTable> theStringTable( + IStringTable::CreateStringTable(m_RenderContext.GetAllocator())); + NVScopedRefCounted<IDOMFactory> theFactory( + IDOMFactory::CreateDOMFactory(m_RenderContext.GetAllocator(), theStringTable)); + eastl::vector<SShaderPreprocessorFeature> theFeatures; + + SDOMElement *theElem = CDOMSerializer::Read(*theFactory, *theInStream).second; + if (theElem) { + NVScopedRefCounted<IDOMReader> theReader = IDOMReader::CreateDOMReader( + m_RenderContext.GetAllocator(), *theElem, theStringTable, theFactory); + QT3DSU32 theAttValue = 0; + theReader->Att("cache_version", theAttValue); + if (theAttValue == IShaderCache::GetShaderVersion()) { + Qt3DSString loadVertexData; + Qt3DSString loadFragmentData; + Qt3DSString loadTessControlData; + Qt3DSString loadTessEvalData; + Qt3DSString loadGeometryData; + Qt3DSString shaderTypeString; + IStringTable &theStringTable(m_RenderContext.GetStringTable()); + for (bool success = theReader->MoveToFirstChild(); success; + success = theReader->MoveToNextSibling()) { + const char8_t *theKeyStr = NULL; + theReader->UnregisteredAtt("key", theKeyStr); + + CRegisteredString theKey = theStringTable.RegisterStr(theKeyStr); + if (theKey.IsValid()) { + m_FlagString.clear(); + const char8_t *theFlagStr = ""; + SShaderCacheProgramFlags theFlags; + if (theReader->UnregisteredAtt("glflags", theFlagStr)) { + m_FlagString.assign(theFlagStr); + theFlags = CacheFlagsToStr(m_FlagString); + } + + m_ContextTypeString.clear(); + if (theReader->UnregisteredAtt("gl-context-type", theFlagStr)) + m_ContextTypeString.assign(theFlagStr); + + theFeatures.clear(); + { + IDOMReader::Scope __featureScope(*theReader); + if (theReader->MoveToFirstChild("Features")) { + for (SDOMAttribute *theAttribute = + theReader->GetFirstAttribute(); + theAttribute; + theAttribute = theAttribute->m_NextAttribute) { + bool featureValue = false; + StringConversion<bool>().StrTo(theAttribute->m_Value, + featureValue); + theFeatures.push_back(SShaderPreprocessorFeature( + theStringTable.RegisterStr( + theAttribute->m_Name.c_str()), + featureValue)); + } + } + } + + qt3ds::render::NVRenderContextType theContextType = + StringToContextType(m_ContextTypeString); + if (((QT3DSU32)theContextType != 0) + && (theContextType & m_RenderContext.GetRenderContextType()) + == theContextType) { + IDOMReader::Scope __readerScope(*theReader); + loadVertexData.clear(); + loadFragmentData.clear(); + loadTessControlData.clear(); + loadTessEvalData.clear(); + loadGeometryData.clear(); + + // Vertex *MUST* be the first + // Todo deal with pure compute shader programs + if (theReader->MoveToFirstChild("VertexCode")) { + const char8_t *theValue = NULL; + theReader->Value(theValue); + loadVertexData.assign(theValue); + while (theReader->MoveToNextSibling()) { + theReader->Value(theValue); + + shaderTypeString.assign( + theReader->GetElementName().c_str()); + ShaderType::Enum shaderType = + StringToShaderType(shaderTypeString); + + if (shaderType == ShaderType::Fragment) + loadFragmentData.assign(theValue); + else if (shaderType == ShaderType::TessControl) + loadTessControlData.assign(theValue); + else if (shaderType == ShaderType::TessEval) + loadTessEvalData.assign(theValue); + else if (shaderType == ShaderType::Geometry) + loadGeometryData.assign(theValue); + } + } + + if (loadVertexData.size() + && (loadFragmentData.size() || loadGeometryData.size())) { + + NVRenderShaderProgram *theShader = ForceCompileProgram( + theKey, loadVertexData.c_str(), loadFragmentData.c_str(), + loadTessControlData.c_str(), loadTessEvalData.c_str(), + loadGeometryData.c_str(), theFlags, + qt3ds::foundation::toDataRef(theFeatures.data(), + (QT3DSU32)theFeatures.size()), + false, true /*fromDisk*/); + // If something doesn't save or load correctly, get the runtime + // to re-generate. + if (!theShader) + m_Shaders.erase(theKey); + } + } + } + } + } + } + } + } + + bool IsShaderCachePersistenceEnabled() const override { return m_ShaderCache != NULL; } + + void SetShaderCompilationEnabled(bool inEnableShaderCompilation) override + { + m_ShaderCompilationEnabled = inEnableShaderCompilation; + } +}; +} + +size_t qt3ds::render::HashShaderFeatureSet(NVConstDataRef<SShaderPreprocessorFeature> inFeatureSet) +{ + size_t retval(0); + for (QT3DSU32 idx = 0, end = inFeatureSet.size(); idx < end; ++idx) { + // From previous implementation, it seems we need to ignore the order of the features. + // But we need to bind the feature flag together with its name, so that the flags will + // influence + // the final hash not only by the true-value count. + retval = retval + ^ (inFeatureSet[idx].m_Name.hash() * eastl::hash<bool>()(inFeatureSet[idx].m_Enabled)); + } + return retval; +} + +bool SShaderPreprocessorFeature::operator<(const SShaderPreprocessorFeature &other) const +{ + return strcmp(m_Name.c_str(), other.m_Name.c_str()) < 0; +} + +bool SShaderPreprocessorFeature::operator==(const SShaderPreprocessorFeature &other) const +{ + return m_Name == other.m_Name && m_Enabled == other.m_Enabled; +} + +IShaderCache &IShaderCache::CreateShaderCache(NVRenderContext &inContext, + IInputStreamFactory &inInputStreamFactory, + IPerfTimer &inPerfTimer) +{ + return *QT3DS_NEW(inContext.GetAllocator(), ShaderCache)(inContext, inInputStreamFactory, + inPerfTimer); +} diff --git a/src/runtimerender/Qt3DSRenderShaderCache.h b/src/runtimerender/Qt3DSRenderShaderCache.h new file mode 100644 index 0000000..47e76f3 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCache.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_CACHE_H +#define QT3DS_RENDER_SHADER_CACHE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace render { + struct ShaderCacheProgramFlagValues + { + enum Enum { + TessellationEnabled = 1 << 0, // tessellation enabled + GeometryShaderEnabled = 1 << 1, // geometry shader enabled + }; + }; + struct SShaderCacheProgramFlags : public NVFlags<ShaderCacheProgramFlagValues::Enum, QT3DSU32> + { + // tessellation enabled + void SetTessellationEnabled(bool inValue) + { + clearOrSet(inValue, ShaderCacheProgramFlagValues::TessellationEnabled); + } + bool IsTessellationEnabled() const + { + return this->operator&(ShaderCacheProgramFlagValues::TessellationEnabled); + } + // geometry shader enabled + void SetGeometryShaderEnabled(bool inValue) + { + clearOrSet(inValue, ShaderCacheProgramFlagValues::GeometryShaderEnabled); + } + bool IsGeometryShaderEnabled() const + { + return this->operator&(ShaderCacheProgramFlagValues::GeometryShaderEnabled); + } + }; + // There are a number of macros used to turn on or off various features. This allows those + // features + // to be propagated into the shader cache's caching mechanism. They will be translated into + //#define name value where value is 1 or zero depending on if the feature is enabled or not. + struct SShaderPreprocessorFeature + { + CRegisteredString m_Name; + bool m_Enabled; + SShaderPreprocessorFeature() + : m_Enabled(false) + { + } + SShaderPreprocessorFeature(CRegisteredString name, bool val) + : m_Name(name) + , m_Enabled(val) + { + } + bool operator<(const SShaderPreprocessorFeature &inOther) const; + bool operator==(const SShaderPreprocessorFeature &inOther) const; + }; + + typedef NVConstDataRef<SShaderPreprocessorFeature> TShaderFeatureSet; + + inline TShaderFeatureSet ShaderCacheNoFeatures() { return TShaderFeatureSet(); } + + // Hash is dependent on the order of the keys; so make sure their order is consistent!! + size_t HashShaderFeatureSet(NVConstDataRef<SShaderPreprocessorFeature> inFeatureSet); + + class IShaderCache : public NVRefCounted + { + protected: + virtual ~IShaderCache() {} + public: + // If directory is nonnull, then we attempt to load any shaders from shadercache.xml in + // inDirectory + // and save any new ones out to the same file. The shaders are marked by the gl version + // used when saving. + // If we can't open shadercache.xml from inDirectory for writing (at least), then we still + // consider the + // shadercache to be disabled. + // This call immediately blocks and attempts to load all applicable shaders from the + // shadercache.xml file in + // the given directory. + virtual void SetShaderCachePersistenceEnabled(const char8_t *inDirectory) = 0; + virtual bool IsShaderCachePersistenceEnabled() const = 0; + // It is up to the caller to ensure that inFeatures contains unique keys. + // It is also up the the caller to ensure the keys are ordered in some way. + virtual NVRenderShaderProgram * + GetProgram(CRegisteredString inKey, + NVConstDataRef<SShaderPreprocessorFeature> inFeatures) = 0; + + // Replace an existing program in the cache for the same key with this program. + // The shaders returned by *CompileProgram functions can be released by this object + // due to ForceCompileProgram or SetProjectDirectory, so clients need to either not + // hold on to them or they need to addref/release them to ensure they still have + // access to them. + // The flags just tell us under what gl state to compile the program in order to hopefully + // reduce program compilations. + // It is up to the caller to ensure that inFeatures contains unique keys. + // It is also up the the caller to ensure the keys are ordered in some way. + virtual NVRenderShaderProgram * + ForceCompileProgram(CRegisteredString inKey, const char8_t *inVert, const char8_t *inFrag, + const char8_t *inTessCtrl, const char8_t *inTessEval, + const char8_t *inGeom, const SShaderCacheProgramFlags &inFlags, + TShaderFeatureSet inFeatures, bool separableProgram, + bool fromDisk = false) = 0; + + // It is up to the caller to ensure that inFeatures contains unique keys. + // It is also up the the caller to ensure the keys are ordered in some way. + virtual NVRenderShaderProgram * + CompileProgram(CRegisteredString inKey, const char8_t *inVert, const char8_t *inFrag, + const char8_t *inTessCtrl, const char8_t *inTessEval, const char8_t *inGeom, + const SShaderCacheProgramFlags &inFlags, TShaderFeatureSet inFeatures, + bool separableProgram = false) = 0; + + // Used to disable any shader compilation during loading. This is used when we are just + // interested in going from uia->binary + // and we expect to run on a headless server of sorts. See the UICCompiler project for its + // only current use case. + virtual void SetShaderCompilationEnabled(bool inEnableShaderCompilation) = 0; + + // Upping the shader version invalidates all previous cache files. + static QT3DSU32 GetShaderVersion() { return 4; } + static const char8_t *GetShaderCacheFileName() { return "shadercache.xml"; } + + static IShaderCache &CreateShaderCache(NVRenderContext &inContext, + IInputStreamFactory &inInputStreamFactory, + IPerfTimer &inPerfTimer); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderShaderCodeGenerator.cpp b/src/runtimerender/Qt3DSRenderShaderCodeGenerator.cpp new file mode 100644 index 0000000..d84c37d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCodeGenerator.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderShaderCodeGenerator.h" + +using namespace qt3ds::render; + +using eastl::make_pair; + +SShaderCodeGeneratorBase::SShaderCodeGeneratorBase(IStringTable &inStringTable, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : m_StringTable(inStringTable) + , m_Codes(alloc, "SShaderCodeGenerator::m_Codes") + , m_Includes(alloc, "SShaderCodeGenerator::m_Includes") + , m_Uniforms(alloc, "SShaderCodeGenerator::m_Uniforms") + , m_ConstantBuffers(alloc, "SShaderCodeGenerator::m_ConstantBuffers") + , m_ConstantBufferParams(alloc, "SShaderCodeGenerator::m_ConstantBufferParams") + , m_Attributes(alloc, "SShaderCodeGenerator::m_Uniforms") + , m_RenderContextType(ctxType) +{ +} +void SShaderCodeGeneratorBase::Begin() +{ + m_Uniforms.clear(); + GetVaryings().clear(); + m_Attributes.clear(); + m_Includes.clear(); + m_Codes.clear(); + m_FinalShaderBuilder.clear(); + m_CodeBuilder.clear(); + m_ConstantBuffers.clear(); + m_ConstantBufferParams.clear(); +} +void SShaderCodeGeneratorBase::Append(const char *data) +{ + m_CodeBuilder.append(data); + m_CodeBuilder.append("\n"); +} +// don't add the newline +void SShaderCodeGeneratorBase::AppendPartial(const char *data) +{ + m_CodeBuilder.append(data); +} +void SShaderCodeGeneratorBase::AddUniform(const char *name, const char *type) +{ + m_Uniforms.insert(make_pair(m_StringTable.RegisterStr(name), m_StringTable.RegisterStr(type))); +} +void SShaderCodeGeneratorBase::AddUniform(TStrType &name, const char *type) +{ + AddUniform(name.c_str(), type); +} + +void SShaderCodeGeneratorBase::AddConstantBuffer(const char *name, const char *layout) +{ + m_ConstantBuffers.insert( + make_pair(m_StringTable.RegisterStr(name), m_StringTable.RegisterStr(layout))); +} +void SShaderCodeGeneratorBase::AddConstantBufferParam(const char *cbName, const char *paramName, + const char *type) +{ + TParamPair theParamPair(m_StringTable.RegisterStr(paramName), m_StringTable.RegisterStr(type)); + TConstantBufferParamPair theBufferParamPair(m_StringTable.RegisterStr(cbName), theParamPair); + m_ConstantBufferParams.push_back(theBufferParamPair); +} +void SShaderCodeGeneratorBase::AddAttribute(const char *name, const char *type) +{ + m_Attributes.insert( + make_pair(m_StringTable.RegisterStr(name), m_StringTable.RegisterStr(type))); +} +void SShaderCodeGeneratorBase::AddAttribute(TStrType &name, const char *type) +{ + AddAttribute(name.c_str(), type); +} +void SShaderCodeGeneratorBase::AddVarying(const char *name, const char *type) +{ + GetVaryings().insert( + make_pair(m_StringTable.RegisterStr(name), m_StringTable.RegisterStr(type))); +} +void SShaderCodeGeneratorBase::AddVarying(TStrType &name, const char *type) +{ + AddVarying(name.c_str(), type); +} +void SShaderCodeGeneratorBase::AddLocalVariable(const char *name, const char *type, int tabCount) +{ + for (; tabCount >= 0; --tabCount) + m_CodeBuilder.append("\t"); + m_CodeBuilder.append(type); + m_CodeBuilder.append(" "); + m_CodeBuilder.append(name); + m_CodeBuilder.append(";\n"); +} + +void SShaderCodeGeneratorBase::AddInclude(const char *name) +{ + m_Includes.insert(m_StringTable.RegisterStr(name)); +} +void SShaderCodeGeneratorBase::AddInclude(TStrType &name) +{ + AddInclude(name.c_str()); +} +void SShaderCodeGeneratorBase::AddLocalVariable(TStrType &name, const char *type, int tabCount) +{ + AddLocalVariable(name.c_str(), type, tabCount); +} +bool SShaderCodeGeneratorBase::HasCode(Enum value) +{ + return m_Codes.contains(value); +} +void SShaderCodeGeneratorBase::SetCode(Enum value) +{ + m_Codes.insert((QT3DSU32)value); +} + +void SShaderCodeGeneratorBase::SetupWorldPosition() +{ + if (!HasCode(WorldPosition)) { + SetCode(WorldPosition); + AddUniform("model_matrix", "mat4"); + Append("\tvec3 varWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + } +} + +void SShaderCodeGeneratorBase::GenerateViewVector() +{ + if (!HasCode(ViewVector)) { + SetCode(ViewVector); + SetupWorldPosition(); + AddInclude("viewProperties.glsllib"); + Append("\tvec3 view_vector = normalize(camera_position - varWorldPos);"); + } +} + +void SShaderCodeGeneratorBase::GenerateWorldNormal() +{ + if (!HasCode(WorldNormal)) { + SetCode(WorldNormal); + AddAttribute("attr_norm", "vec3"); + AddUniform("normal_matrix", "mat3"); + Append("\tvec3 world_normal = normalize(normal_matrix * objectNormal).xyz;"); + } +} + +void SShaderCodeGeneratorBase::GenerateEnvMapReflection(SShaderCodeGeneratorBase &inFragmentShader) +{ + if (!HasCode(EnvMapReflection)) { + SetCode(EnvMapReflection); + SetupWorldPosition(); + GenerateWorldNormal(); + AddInclude("viewProperties.glsllib"); + AddVarying("var_object_to_camera", "vec3"); + Append("\tvar_object_to_camera = normalize( varWorldPos - camera_position );"); + // World normal cannot be relied upon in the vertex shader because of bump maps. + inFragmentShader.Append("\tvec3 environment_map_reflection = reflect( " + "vec3(var_object_to_camera.x, var_object_to_camera.y, " + "var_object_to_camera.z), world_normal.xyz );"); + inFragmentShader.Append("\tenvironment_map_reflection *= vec3( 0.5, 0.5, 0 );"); + inFragmentShader.Append("\tenvironment_map_reflection += vec3( 0.5, 0.5, 1.0 );"); + } +} + +void SShaderCodeGeneratorBase::GenerateUVCoords() +{ + if (!HasCode(UVCoords)) { + SetCode(UVCoords); + AddAttribute("attr_uv0", "vec2"); + Append("\tvec2 uv_coords = attr_uv0;"); + } +} + +void SShaderCodeGeneratorBase::GenerateTextureSwizzle(NVRenderTextureSwizzleMode::Enum swizzleMode, + eastl::basic_string<char8_t> &texSwizzle, + eastl::basic_string<char8_t> &lookupSwizzle) +{ + qt3ds::render::NVRenderContextType deprecatedContextFlags(NVRenderContextValues::GL2 + | NVRenderContextValues::GLES2); + + if (!(m_RenderContextType & deprecatedContextFlags)) { + switch (swizzleMode) { + case NVRenderTextureSwizzleMode::L8toR8: + case NVRenderTextureSwizzleMode::L16toR16: + texSwizzle.append(".rgb"); + lookupSwizzle.append(".rrr"); + break; + case NVRenderTextureSwizzleMode::L8A8toRG8: + texSwizzle.append(".rgba"); + lookupSwizzle.append(".rrrg"); + break; + case NVRenderTextureSwizzleMode::A8toR8: + texSwizzle.append(".a"); + lookupSwizzle.append(".r"); + break; + default: + break; + } + } +} + +void SShaderCodeGeneratorBase::GenerateShadedWireframeBase() +{ + // how this all work see + // http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf + Append("// project points to screen space\n" + "\tvec3 p0 = vec3(viewport_matrix * (gl_in[0].gl_Position / gl_in[0].gl_Position.w));\n" + "\tvec3 p1 = vec3(viewport_matrix * (gl_in[1].gl_Position / gl_in[1].gl_Position.w));\n" + "\tvec3 p2 = vec3(viewport_matrix * (gl_in[2].gl_Position / gl_in[2].gl_Position.w));\n" + "// compute triangle heights\n" + "\tfloat e1 = length(p1 - p2);\n" + "\tfloat e2 = length(p2 - p0);\n" + "\tfloat e3 = length(p1 - p0);\n" + "\tfloat alpha = acos( (e2*e2 + e3*e3 - e1*e1) / (2.0*e2*e3) );\n" + "\tfloat beta = acos( (e1*e1 + e3*e3 - e2*e2) / (2.0*e1*e3) );\n" + "\tfloat ha = abs( e3 * sin( beta ) );\n" + "\tfloat hb = abs( e3 * sin( alpha ) );\n" + "\tfloat hc = abs( e2 * sin( alpha ) );\n"); +} + +void SShaderCodeGeneratorBase::AddShaderItemMap(const char *itemType, + const TStrTableStrMap &itemMap) +{ + m_FinalShaderBuilder.append("\n"); + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); iter != end; + ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(";\n"); + } +} + +void SShaderCodeGeneratorBase::AddShaderConstantBufferItemMap( + const char *itemType, const TStrTableStrMap &cbMap, TConstantBufferParamArray cbParamsArray) +{ + m_FinalShaderBuilder.append("\n"); + + // iterate over all constant buffers + for (TStrTableStrMap::const_iterator iter = cbMap.begin(), end = cbMap.end(); iter != end; + ++iter) { + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(" {\n"); + // iterate over all param entries and add match + for (TConstantBufferParamArray::const_iterator iter1 = cbParamsArray.begin(), + end = cbParamsArray.end(); + iter1 != end; ++iter1) { + if (iter1->first == iter->first) { + m_FinalShaderBuilder.append(iter1->second.second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter1->second.first); + m_FinalShaderBuilder.append(";\n"); + } + } + + m_FinalShaderBuilder.append("};\n"); + } +} + +const char *SShaderCodeGeneratorBase::BuildShaderSource() +{ + for (nvhash_set<CRegisteredString>::const_iterator iter = m_Includes.begin(), + end = m_Includes.end(); + iter != end; ++iter) { + m_FinalShaderBuilder.append("#include \""); + m_FinalShaderBuilder.append(iter->c_str()); + m_FinalShaderBuilder.append("\"\n"); + } + AddShaderItemMap("attribute", m_Attributes); + AddShaderItemMap("uniform", m_Uniforms); + AddShaderConstantBufferItemMap("uniform", m_ConstantBuffers, m_ConstantBufferParams); + AddShaderItemMap("varying", GetVaryings()); + m_FinalShaderBuilder.append("\n"); + m_FinalShaderBuilder.append(m_CodeBuilder.c_str()); + return m_FinalShaderBuilder.c_str(); +} +SShaderCodeGeneratorBase &SShaderCodeGeneratorBase::operator<<(const char *data) +{ + m_CodeBuilder.append(data); + return *this; +} +SShaderCodeGeneratorBase &SShaderCodeGeneratorBase::operator<<(const TStrType &data) +{ + m_CodeBuilder.append(data); + return *this; +} + +SShaderCodeGeneratorBase &SShaderCodeGeneratorBase::operator<<(const SEndlType & /*data*/) +{ + m_CodeBuilder.append("\n"); + return *this; +} + +SShaderVertexCodeGenerator::SShaderVertexCodeGenerator(IStringTable &inStringTable, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : SShaderCodeGeneratorBase(inStringTable, alloc, ctxType) + , m_Varyings(alloc, "SShaderVertexCodeGenerator::m_Varyings") +{ +} +TStrTableStrMap &SShaderVertexCodeGenerator::GetVaryings() +{ + return m_Varyings; +} + +SShaderTessControlCodeGenerator::SShaderTessControlCodeGenerator( + SShaderVertexCodeGenerator &vert, NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : SShaderCodeGeneratorBase(vert.m_StringTable, alloc, ctxType) + , m_VertGenerator(vert) + , m_Varyings(alloc, "SShaderTessControlCodeGenerator::m_Varyings") +{ +} + +// overwritten from base +void SShaderTessControlCodeGenerator::AddShaderItemMap(const char *itemType, + const TStrTableStrMap &itemMap) +{ + eastl::string extVtx(""); + eastl::string extTC(""); + eastl::string type(itemType); + if (!type.compare("varying")) { + extVtx = "[]"; + extTC = "TC[]"; + itemType = "attribute"; + } + + m_FinalShaderBuilder.append("\n"); + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); iter != end; + ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(extVtx.c_str()); + m_FinalShaderBuilder.append(";\n"); + } + + // if this is varyings write output of tess control shader + if (!extVtx.empty()) { + m_FinalShaderBuilder.append("\n"); + itemType = "varying"; + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); + iter != end; ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(extTC.c_str()); + m_FinalShaderBuilder.append(";\n"); + } + } +} +TStrTableStrMap &SShaderTessControlCodeGenerator::GetVaryings() +{ + return m_VertGenerator.m_Varyings; +} + +SShaderTessEvalCodeGenerator::SShaderTessEvalCodeGenerator(SShaderTessControlCodeGenerator &tc, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : SShaderCodeGeneratorBase(tc.m_StringTable, alloc, ctxType) + , m_TessControlGenerator(tc) + , m_hasGeometryStage(false) +{ +} +// overwritten from base +void SShaderTessEvalCodeGenerator::AddShaderItemMap(const char *itemType, + const TStrTableStrMap &itemMap) +{ + eastl::string extTC(""); + eastl::string extTE(""); + eastl::string type(itemType); + if (!type.compare("varying")) { + extTC = "TC[]"; + itemType = "attribute"; + } + if (m_hasGeometryStage) { + extTE = "TE"; + } + + m_FinalShaderBuilder.append("\n"); + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); iter != end; + ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(extTC.c_str()); + m_FinalShaderBuilder.append(";\n"); + } + + // if this are varyings write output of tess eval shader + if (!extTC.empty()) { + m_FinalShaderBuilder.append("\n"); + itemType = "varying"; + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); + iter != end; ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(extTE.c_str()); + m_FinalShaderBuilder.append(";\n"); + } + } +} +TStrTableStrMap &SShaderTessEvalCodeGenerator::GetVaryings() +{ + return m_TessControlGenerator.m_VertGenerator.GetVaryings(); +} +void SShaderTessEvalCodeGenerator::SetGeometryStage(bool hasGeometryStage) +{ + m_hasGeometryStage = hasGeometryStage; +} + +SShaderGeometryCodeGenerator::SShaderGeometryCodeGenerator(SShaderVertexCodeGenerator &vert, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : SShaderCodeGeneratorBase(vert.m_StringTable, alloc, ctxType) + , m_VertGenerator(vert) + , m_hasTessellationStage(true) +{ +} + +// overwritten from base +void SShaderGeometryCodeGenerator::AddShaderItemMap(const char *itemType, + const TStrTableStrMap &itemMap) +{ + eastl::string inExt(""); + eastl::string type(itemType); + if (!type.compare("varying")) { + itemType = "attribute"; + if (m_hasTessellationStage) + inExt = "TE[]"; + else + inExt = "[]"; + } + + m_FinalShaderBuilder.append("\n"); + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); iter != end; + ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(inExt.c_str()); + m_FinalShaderBuilder.append(";\n"); + } + + // if this are varyings write output of geometry shader + if (!type.compare("varying")) { + m_FinalShaderBuilder.append("\n"); + itemType = "varying"; + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); + iter != end; ++iter) { + m_FinalShaderBuilder.append(itemType); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->second); + m_FinalShaderBuilder.append(" "); + m_FinalShaderBuilder.append(iter->first); + m_FinalShaderBuilder.append(";\n"); + } + } +} +TStrTableStrMap &SShaderGeometryCodeGenerator::GetVaryings() +{ + return m_VertGenerator.m_Varyings; +} +void SShaderGeometryCodeGenerator::SetTessellationStage(bool hasTessellationStage) +{ + m_hasTessellationStage = hasTessellationStage; +} + +SShaderFragmentCodeGenerator::SShaderFragmentCodeGenerator(SShaderVertexCodeGenerator &vert, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType) + : SShaderCodeGeneratorBase(vert.m_StringTable, alloc, ctxType) + , m_VertGenerator(vert) +{ +} +TStrTableStrMap &SShaderFragmentCodeGenerator::GetVaryings() +{ + return m_VertGenerator.m_Varyings; +} diff --git a/src/runtimerender/Qt3DSRenderShaderCodeGenerator.h b/src/runtimerender/Qt3DSRenderShaderCodeGenerator.h new file mode 100644 index 0000000..d68a369 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCodeGenerator.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_CODE_GENERATOR_H +#define QT3DS_RENDER_SHADER_CODE_GENERATOR_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSContainers.h" +#include "EASTL/string.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "StringTools.h" + +namespace qt3ds { +namespace render { + + struct SEndlType + { + }; + extern SEndlType Endl; + + typedef std::basic_string<char> TStrType; + typedef eastl::pair<CRegisteredString, CRegisteredString> TParamPair; + typedef eastl::pair<CRegisteredString, TParamPair> TConstantBufferParamPair; + typedef nvvector<TConstantBufferParamPair> TConstantBufferParamArray; + typedef nvhash_map<CRegisteredString, CRegisteredString> TStrTableStrMap; + + struct SShaderCodeGeneratorBase + { + enum Enum { + Unknown = 0, + Lighting, + ViewVector, + WorldNormal, + WorldPosition, + EnvMapReflection, + UVCoords, + }; + IStringTable &m_StringTable; + nvhash_set<QT3DSU32> m_Codes; // set of enums we have included. + nvhash_set<CRegisteredString> m_Includes; + TStrTableStrMap m_Uniforms; + TStrTableStrMap m_ConstantBuffers; + TConstantBufferParamArray m_ConstantBufferParams; + TStrTableStrMap m_Attributes; + Qt3DSString m_FinalShaderBuilder; + TStrType m_CodeBuilder; + qt3ds::render::NVRenderContextType m_RenderContextType; + + SShaderCodeGeneratorBase(IStringTable &inStringTable, NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + virtual TStrTableStrMap &GetVaryings() = 0; + void Begin(); + void Append(const char *data); + // don't add the newline + void AppendPartial(const char *data); + void AddConstantBuffer(const char *name, const char *layout); + void AddConstantBufferParam(const char *cbName, const char *paramName, const char *type); + void AddUniform(const char *name, const char *type); + void AddUniform(TStrType &name, const char *type); + void AddAttribute(const char *name, const char *type); + void AddAttribute(TStrType &name, const char *type); + void AddVarying(const char *name, const char *type); + void AddVarying(TStrType &name, const char *type); + void AddLocalVariable(const char *name, const char *type, int tabCount = 1); + void AddLocalVariable(TStrType &name, const char *type, int tabCount = 1); + void AddInclude(const char *name); + void AddInclude(TStrType &name); + bool HasCode(Enum value); + void SetCode(Enum value); + void SetupWorldPosition(); + void GenerateViewVector(); + void GenerateWorldNormal(); + void GenerateEnvMapReflection(SShaderCodeGeneratorBase &inFragmentShader); + void GenerateUVCoords(); + void GenerateTextureSwizzle(NVRenderTextureSwizzleMode::Enum swizzleMode, + eastl::basic_string<char8_t> &texSwizzle, + eastl::basic_string<char8_t> &lookupSwizzle); + void GenerateShadedWireframeBase(); + void AddLighting(); + const char *BuildShaderSource(); + SShaderCodeGeneratorBase &operator<<(const char *data); + SShaderCodeGeneratorBase &operator<<(const TStrType &data); + SShaderCodeGeneratorBase &operator<<(const SEndlType & /*data*/); + + protected: + virtual void AddShaderItemMap(const char *itemType, const TStrTableStrMap &itemMap); + void AddShaderConstantBufferItemMap(const char *itemType, const TStrTableStrMap &cbMap, + TConstantBufferParamArray cbParamsArray); + }; + + struct SShaderVertexCodeGenerator : public SShaderCodeGeneratorBase + { + TStrTableStrMap m_Varyings; + SShaderVertexCodeGenerator(IStringTable &inStringTable, NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + TStrTableStrMap &GetVaryings() override; + }; + + struct SShaderTessControlCodeGenerator : public SShaderCodeGeneratorBase + { + SShaderVertexCodeGenerator &m_VertGenerator; + TStrTableStrMap m_Varyings; + SShaderTessControlCodeGenerator(SShaderVertexCodeGenerator &vert, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + + void AddShaderItemMap(const char *itemType, const TStrTableStrMap &itemMap) override; + TStrTableStrMap &GetVaryings() override; + }; + + struct SShaderTessEvalCodeGenerator : public SShaderCodeGeneratorBase + { + SShaderTessControlCodeGenerator &m_TessControlGenerator; + bool m_hasGeometryStage; + + SShaderTessEvalCodeGenerator(SShaderTessControlCodeGenerator &tc, + NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + + void AddShaderItemMap(const char *itemType, const TStrTableStrMap &itemMap) override; + TStrTableStrMap &GetVaryings() override; + virtual void SetGeometryStage(bool hasGeometryStage); + }; + + struct SShaderGeometryCodeGenerator : public SShaderCodeGeneratorBase + { + SShaderVertexCodeGenerator &m_VertGenerator; + bool m_hasTessellationStage; + + SShaderGeometryCodeGenerator(SShaderVertexCodeGenerator &vert, NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + + void AddShaderItemMap(const char *itemType, const TStrTableStrMap &itemMap) override; + TStrTableStrMap &GetVaryings() override; + virtual void SetTessellationStage(bool hasTessellationStage); + }; + + struct SShaderFragmentCodeGenerator : public SShaderCodeGeneratorBase + { + SShaderVertexCodeGenerator &m_VertGenerator; + SShaderFragmentCodeGenerator(SShaderVertexCodeGenerator &vert, NVAllocatorCallback &alloc, + qt3ds::render::NVRenderContextType ctxType); + TStrTableStrMap &GetVaryings() override; + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.cpp b/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.cpp new file mode 100644 index 0000000..cfdf47a --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.cpp @@ -0,0 +1,671 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Utils.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderDynamicObjectSystem.h" + +#include <QtGui/qopengl.h> + +using namespace qt3ds::render; + +namespace { +struct SStageGeneratorBase : public IShaderStageGenerator +{ + NVFoundationBase &m_Foundation; + IStringTable &m_StringTable; + TStrTableStrMap m_Incoming; + TStrTableStrMap *m_Outgoing; + nvhash_set<CRegisteredString> m_Includes; + TStrTableStrMap m_Uniforms; + TStrTableStrMap m_ConstantBuffers; + TConstantBufferParamArray m_ConstantBufferParams; + Qt3DSString m_CodeBuilder; + Qt3DSString m_FinalBuilder; + ShaderGeneratorStages::Enum m_Stage; + TShaderGeneratorStageFlags m_EnabledStages; + QStringList m_addedFunctions; + + SStageGeneratorBase(NVFoundationBase &inFnd, IStringTable &strTable, + ShaderGeneratorStages::Enum inStage) + + : m_Foundation(inFnd) + , m_StringTable(strTable) + , m_Incoming(inFnd.getAllocator(), "m_Incoming") + , m_Outgoing(NULL) + , m_Includes(inFnd.getAllocator(), "m_Includes") + , m_Uniforms(inFnd.getAllocator(), "m_Uniforms") + , m_ConstantBuffers(inFnd.getAllocator(), "m_ConstantBuffers") + , m_ConstantBufferParams(inFnd.getAllocator(), "m_ConstantBufferParams") + , m_Stage(inStage) + { + } + + virtual void Begin(TShaderGeneratorStageFlags inEnabledStages) + { + m_Incoming.clear(); + m_Outgoing = NULL; + m_Includes.clear(); + m_Uniforms.clear(); + m_ConstantBuffers.clear(); + m_ConstantBufferParams.clear(); + m_CodeBuilder.clear(); + m_FinalBuilder.clear(); + m_EnabledStages = inEnabledStages; + m_addedFunctions.clear(); + // the shared buffers will be cleared elsewhere. + } + + CRegisteredString Str(const char8_t *var) { return m_StringTable.RegisterStr(var); } + + void AddIncoming(const char8_t *name, const char8_t *type) override + { + m_Incoming.insert(eastl::make_pair(Str(name), Str(type))); + } + + virtual const char8_t *GetIncomingVariableName() + { + return "in"; + } + + void AddIncoming(const TStrType &name, const char8_t *type) override + { + AddIncoming(name.c_str(), type); + } + + void AddIncoming(const QString &name, const char8_t *type) override + { + AddIncoming(name.toUtf8().constData(), type); + } + + void AddOutgoing(const char8_t *name, const char8_t *type) override + { + if (m_Outgoing == NULL) { + QT3DS_ASSERT(false); + return; + } + m_Outgoing->insert(eastl::make_pair(Str(name), Str(type))); + } + + void AddOutgoing(const TStrType &name, const char8_t *type) override + { + AddOutgoing(name.c_str(), type); + } + + void AddOutgoing(const QString &name, const char8_t *type) override + { + AddOutgoing(name.toUtf8().constData(), type); + } + + void AddUniform(const char8_t *name, const char8_t *type) override + { + m_Uniforms.insert(eastl::make_pair(Str(name), Str(type))); + } + + void AddUniform(const TStrType &name, const char8_t *type) override + { + AddUniform(name.c_str(), type); + } + + void AddUniform(const QString &name, const char8_t *type) override + { + AddUniform(name.toUtf8().constData(), type); + } + + void AddConstantBuffer(const char *name, const char *layout) override + { + m_ConstantBuffers.insert(eastl::make_pair(Str(name), Str(layout))); + } + + void AddConstantBuffer(const QString &name, const char *layout) override + { + AddConstantBuffer(name.toUtf8().constData(), layout); + } + + void AddConstantBufferParam(const char *cbName, const char *paramName, + const char *type) override + { + TParamPair theParamPair(m_StringTable.RegisterStr(paramName), + m_StringTable.RegisterStr(type)); + TConstantBufferParamPair theBufferParamPair(m_StringTable.RegisterStr(cbName), + theParamPair); + m_ConstantBufferParams.push_back(theBufferParamPair); + } + + void AddConstantBufferParam(const QString &cbName, const QString ¶mName, + const char *type) override + { + AddConstantBufferParam(cbName.toUtf8().constData(), paramName.toUtf8().constData(), type); + } + + IShaderStageGenerator &operator<<(const char *data) override + { + m_CodeBuilder.append(nonNull(data)); + return *this; + } + + IShaderStageGenerator &operator<<(const TStrType &data) override + { + m_CodeBuilder.append(data.c_str()); + return *this; + } + + IShaderStageGenerator &operator<<(const QString &data) override + { + m_CodeBuilder.append(data); + return *this; + } + IShaderStageGenerator &operator<<(const SEndlType & /*data*/) override + { + m_CodeBuilder.append("\n"); + return *this; + } + void Append(const char *data) override + { + m_CodeBuilder.append(nonNull(data)); + m_CodeBuilder.append("\n"); + } + void AppendPartial(const char *data) override { m_CodeBuilder.append(nonNull(data)); } + ShaderGeneratorStages::Enum Stage() const override { return m_Stage; } + + virtual void AddShaderItemMap(const char *itemType, const TStrTableStrMap &itemMap, + const char8_t *inItemSuffix = "") + { + m_FinalBuilder.append("\n"); + + for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); + iter != end; ++iter) { + m_FinalBuilder.append(itemType); + m_FinalBuilder.append(" "); + m_FinalBuilder.append(iter->second); + m_FinalBuilder.append(" "); + m_FinalBuilder.append(iter->first); + m_FinalBuilder.append(inItemSuffix); + m_FinalBuilder.append(";\n"); + } + } + + virtual void AddShaderIncomingMap() { AddShaderItemMap(GetIncomingVariableName(), m_Incoming); } + + virtual void AddShaderUniformMap() { AddShaderItemMap("uniform", m_Uniforms); } + + virtual void AddShaderOutgoingMap() + { + if (m_Outgoing) + AddShaderItemMap("varying", *m_Outgoing); + } + + virtual void AddShaderConstantBufferItemMap(const char *itemType, const TStrTableStrMap &cbMap, + TConstantBufferParamArray cbParamsArray) + { + m_FinalBuilder.append("\n"); + + // iterate over all constant buffers + for (TStrTableStrMap::const_iterator iter = cbMap.begin(), end = cbMap.end(); iter != end; + ++iter) { + m_FinalBuilder.append(iter->second); + m_FinalBuilder.append(" "); + m_FinalBuilder.append(itemType); + m_FinalBuilder.append(" "); + m_FinalBuilder.append(iter->first); + m_FinalBuilder.append(" {\n"); + // iterate over all param entries and add match + for (TConstantBufferParamArray::const_iterator iter1 = cbParamsArray.begin(), + end = cbParamsArray.end(); + iter1 != end; ++iter1) { + if (iter1->first == iter->first) { + m_FinalBuilder.append(iter1->second.second); + m_FinalBuilder.append(" "); + m_FinalBuilder.append(iter1->second.first); + m_FinalBuilder.append(";\n"); + } + } + + m_FinalBuilder.append("};\n"); + } + } + + virtual void AppendShaderCode() { m_FinalBuilder.append(m_CodeBuilder); } + + virtual void UpdateShaderCacheFlags(SShaderCacheProgramFlags &) {} + + void AddInclude(const char8_t *name) override { m_Includes.insert(Str(name)); } + + void AddInclude(const TStrType &name) override { AddInclude(name.c_str()); } + + void AddInclude(const QString &name) override + { + QByteArray arr = name.toLatin1(); + AddInclude(arr.data()); + } + + virtual const char8_t *BuildShaderSource() + { + for (nvhash_set<CRegisteredString>::const_iterator iter = m_Includes.begin(), + end = m_Includes.end(); + iter != end; ++iter) { + m_FinalBuilder.append("#include \""); + m_FinalBuilder.append(iter->c_str()); + m_FinalBuilder.append("\"\n"); + } + AddShaderIncomingMap(); + AddShaderUniformMap(); + AddShaderConstantBufferItemMap("uniform", m_ConstantBuffers, m_ConstantBufferParams); + AddShaderOutgoingMap(); + m_FinalBuilder.append("\n"); + AppendShaderCode(); + return m_FinalBuilder.c_str(); + } + + void AddFunction(const QString &functionName) override + { + if (!m_addedFunctions.contains(functionName)) { + m_addedFunctions.push_back(functionName); + QString includeName; + QTextStream stream(&includeName); + stream << "func" << functionName << ".glsllib"; + AddInclude(includeName); + } + } +}; + +struct SVertexShaderGenerator : public SStageGeneratorBase +{ + SVertexShaderGenerator(NVFoundationBase &inFnd, IStringTable &strTable) + : SStageGeneratorBase(inFnd, strTable, ShaderGeneratorStages::Vertex) + { + } + + const char8_t *GetIncomingVariableName() override { return "attribute"; } + virtual void AddIncomingInterpolatedMap() {} + + virtual const char8_t *GetInterpolatedIncomingSuffix() const { return "_attr"; } + virtual const char8_t *GetInterpolatedOutgoingSuffix() const { return ""; } +}; + +struct STessControlShaderGenerator : public SStageGeneratorBase +{ + STessControlShaderGenerator(NVFoundationBase &inFnd, IStringTable &strTable) + : SStageGeneratorBase(inFnd, strTable, ShaderGeneratorStages::TessControl) + { + } + + void AddShaderIncomingMap() override { AddShaderItemMap("attribute", m_Incoming, "[]"); } + + void AddShaderOutgoingMap() override + { + if (m_Outgoing) + AddShaderItemMap("varying", *m_Outgoing, "[]"); + } + + void UpdateShaderCacheFlags(SShaderCacheProgramFlags &inFlags) override + { + inFlags.SetTessellationEnabled(true); + } +}; + +struct STessEvalShaderGenerator : public SStageGeneratorBase +{ + STessEvalShaderGenerator(NVFoundationBase &inFnd, IStringTable &strTable) + : SStageGeneratorBase(inFnd, strTable, ShaderGeneratorStages::TessEval) + { + } + + void AddShaderIncomingMap() override { AddShaderItemMap("attribute", m_Incoming, "[]"); } + + void UpdateShaderCacheFlags(SShaderCacheProgramFlags &inFlags) override + { + inFlags.SetTessellationEnabled(true); + } +}; + +struct SGeometryShaderGenerator : public SStageGeneratorBase +{ + SGeometryShaderGenerator(NVFoundationBase &inFnd, IStringTable &strTable) + : SStageGeneratorBase(inFnd, strTable, ShaderGeneratorStages::Geometry) + { + } + + void AddShaderIncomingMap() override { AddShaderItemMap("attribute", m_Incoming, "[]"); } + + void AddShaderOutgoingMap() override + { + if (m_Outgoing) + AddShaderItemMap("varying", *m_Outgoing); + } + void UpdateShaderCacheFlags(SShaderCacheProgramFlags &inFlags) override + { + inFlags.SetGeometryShaderEnabled(true); + } +}; + +struct SFragmentShaderGenerator : public SStageGeneratorBase +{ + SFragmentShaderGenerator(NVFoundationBase &inFnd, IStringTable &strTable) + : SStageGeneratorBase(inFnd, strTable, ShaderGeneratorStages::Fragment) + { + } + void AddShaderIncomingMap() override { AddShaderItemMap("varying", m_Incoming); } + void AddShaderOutgoingMap() override {} +}; + +struct SShaderGeneratedProgramOutput +{ + // never null; so safe to call strlen on. + const char8_t *m_VertexShader; + const char8_t *m_TessControlShader; + const char8_t *m_TessEvalShader; + const char8_t *m_GeometryShader; + const char8_t *m_FragmentShader; + + SShaderGeneratedProgramOutput() + : m_VertexShader("") + , m_TessControlShader("") + , m_TessEvalShader("") + , m_GeometryShader("") + , m_FragmentShader("") + { + } + + SShaderGeneratedProgramOutput(const char8_t *vs, const char8_t *tc, const char8_t *te, + const char8_t *gs, const char8_t *fs) + : m_VertexShader(vs) + , m_TessControlShader(tc) + , m_TessEvalShader(te) + , m_GeometryShader(gs) + , m_FragmentShader(fs) + { + } +}; + +struct SProgramGenerator : public IShaderProgramGenerator +{ + IQt3DSRenderContext &m_Context; + SVertexShaderGenerator m_VS; + STessControlShaderGenerator m_TC; + STessEvalShaderGenerator m_TE; + SGeometryShaderGenerator m_GS; + SFragmentShaderGenerator m_FS; + + TShaderGeneratorStageFlags m_EnabledStages; + + QT3DSI32 m_RefCount; + + SProgramGenerator(IQt3DSRenderContext &inContext) + : m_Context(inContext) + , m_VS(inContext.GetFoundation(), inContext.GetStringTable()) + , m_TC(inContext.GetFoundation(), inContext.GetStringTable()) + , m_TE(inContext.GetFoundation(), inContext.GetStringTable()) + , m_GS(inContext.GetFoundation(), inContext.GetStringTable()) + , m_FS(inContext.GetFoundation(), inContext.GetStringTable()) + , m_RefCount(0) + { + } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + NVFoundationBase &theFoundation(m_Context.GetFoundation()); + NVDelete(theFoundation.getAllocator(), this); + } + } + + void LinkStages() + { + // Link stages incoming to outgoing variables. + SStageGeneratorBase *previous = NULL; + QT3DSU32 theStageId = 1; + for (QT3DSU32 idx = 0, end = (QT3DSU32)ShaderGeneratorStages::StageCount; idx < end; + ++idx, theStageId = theStageId << 1) { + SStageGeneratorBase *thisStage = NULL; + ShaderGeneratorStages::Enum theStageEnum = + static_cast<ShaderGeneratorStages::Enum>(theStageId); + if ((m_EnabledStages & theStageEnum)) { + thisStage = &InternalGetStage(theStageEnum); + if (previous) + previous->m_Outgoing = &thisStage->m_Incoming; + previous = thisStage; + } + } + } + + void BeginProgram(TShaderGeneratorStageFlags inEnabledStages) override + { + m_VS.Begin(inEnabledStages); + m_TC.Begin(inEnabledStages); + m_TE.Begin(inEnabledStages); + m_GS.Begin(inEnabledStages); + m_FS.Begin(inEnabledStages); + m_EnabledStages = inEnabledStages; + LinkStages(); + } + + TShaderGeneratorStageFlags GetEnabledStages() const override { return m_EnabledStages; } + + SStageGeneratorBase &InternalGetStage(ShaderGeneratorStages::Enum inStage) + { + switch (inStage) { + case ShaderGeneratorStages::Vertex: + return m_VS; + case ShaderGeneratorStages::TessControl: + return m_TC; + case ShaderGeneratorStages::TessEval: + return m_TE; + case ShaderGeneratorStages::Geometry: + return m_GS; + case ShaderGeneratorStages::Fragment: + return m_FS; + default: + QT3DS_ASSERT(false); + break; + } + return m_VS; + } + // get the stage or NULL if it has not been created. + IShaderStageGenerator *GetStage(ShaderGeneratorStages::Enum inStage) override + { + if (inStage > 0 || inStage < ShaderGeneratorStages::StageCount) { + if ((m_EnabledStages & inStage)) + return &InternalGetStage(inStage); + } else { + QT3DS_ASSERT(false); + } + return NULL; + } + + qt3ds::render::NVRenderShaderProgram * + CompileGeneratedShader(const char *inShaderName, const SShaderCacheProgramFlags &inFlags, + TShaderFeatureSet inFeatureSet, bool separableProgram) override + { + // No stages enabled + if (((QT3DSU32)m_EnabledStages) == 0) { + QT3DS_ASSERT(false); + return NULL; + } + + qt3ds::render::IDynamicObjectSystem &theDynamicSystem(m_Context.GetDynamicObjectSystem()); + SShaderCacheProgramFlags theCacheFlags(inFlags); + for (QT3DSU32 stageIdx = 0, stageEnd = ShaderGeneratorStages::StageCount; stageIdx < stageEnd; + ++stageIdx) { + ShaderGeneratorStages::Enum stageName = + static_cast<ShaderGeneratorStages::Enum>(1 << stageIdx); + if (m_EnabledStages & stageName) { + SStageGeneratorBase &theStage(InternalGetStage(stageName)); + theStage.BuildShaderSource(); + theStage.UpdateShaderCacheFlags(theCacheFlags); + theDynamicSystem.InsertShaderHeaderInformation(theStage.m_FinalBuilder, + inShaderName); + } + } + + const char *vertexShaderSource = m_VS.m_FinalBuilder.c_str(); + const char *tcShaderSource = m_TC.m_FinalBuilder.c_str(); + const char *teShaderSource = m_TE.m_FinalBuilder.c_str(); + const char *geShaderSource = m_GS.m_FinalBuilder.c_str(); + const char *fragmentShaderSource = m_FS.m_FinalBuilder.c_str(); + + IShaderCache &theCache = m_Context.GetShaderCache(); + CRegisteredString theCacheKey = m_Context.GetStringTable().RegisterStr(inShaderName); + return theCache.CompileProgram(theCacheKey, vertexShaderSource, fragmentShaderSource, + tcShaderSource, teShaderSource, geShaderSource, + theCacheFlags, inFeatureSet, separableProgram); + } +}; +}; + +IShaderProgramGenerator & +IShaderProgramGenerator::CreateProgramGenerator(IQt3DSRenderContext &inContext) +{ + return *QT3DS_NEW(inContext.GetAllocator(), SProgramGenerator)(inContext); +} + +void IShaderProgramGenerator::OutputParaboloidDepthVertex(IShaderStageGenerator &vertexShader) +{ + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddInclude("shadowMapping.glsllib"); + vertexShader.AddUniform("model_view_projection", "mat4"); + // vertexShader.AddUniform("model_view", "mat4"); + vertexShader.AddUniform("camera_properties", "vec2"); + // vertexShader.AddOutgoing("view_pos", "vec4"); + vertexShader.AddOutgoing("world_pos", "vec4"); + + // Project the location onto screen space. + // This will be horrible if you have a single large polygon. Tessellation is your friend here! + vertexShader.Append("void main() {"); + vertexShader.Append( + " ParaboloidMapResult data = VertexParaboloidDepth( attr_pos, model_view_projection );"); + vertexShader.Append(" gl_Position = data.m_Position;"); + vertexShader.Append(" world_pos = data.m_WorldPos;"); + vertexShader.Append("}"); +} + +void IShaderProgramGenerator::OutputParaboloidDepthTessEval(IShaderStageGenerator &tessEvalShader) +{ + tessEvalShader.AddInclude("shadowMapping.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append(" ParaboloidMapResult data = VertexParaboloidDepth( vec3(pos.xyz), " + "model_view_projection );"); + tessEvalShader.Append(" gl_Position = data.m_Position;"); + tessEvalShader.Append(" world_pos = data.m_WorldPos;"); +} + +void IShaderProgramGenerator::OutputParaboloidDepthFragment(IShaderStageGenerator &fragmentShader) +{ + fragmentShader.AddInclude("shadowMappingFragment.glsllib"); + fragmentShader.AddUniform("model_view_projection", "mat4"); + fragmentShader.AddUniform("camera_properties", "vec2"); + fragmentShader.Append("void main() {"); + fragmentShader.Append(" gl_FragDepth = FragmentParaboloidDepth( world_pos, " + "model_view_projection, camera_properties );"); + fragmentShader.Append("}"); +} + +void IShaderProgramGenerator::OutputCubeFaceDepthVertex(IShaderStageGenerator &vertexShader) +{ + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.AddOutgoing("raw_pos", "vec4"); + vertexShader.AddOutgoing("world_pos", "vec4"); + + vertexShader.Append("void main() {"); + vertexShader.Append(" world_pos = model_matrix * vec4( attr_pos, 1.0 );"); + vertexShader.Append(" world_pos /= world_pos.w;"); + vertexShader.Append(" gl_Position = model_view_projection * vec4( attr_pos, 1.0 );"); + vertexShader.Append(" raw_pos = vec4( attr_pos, 1.0 );"); + // vertexShader.Append(" gl_Position = vec4( attr_pos, 1.0 );"); + vertexShader.Append("}"); +} + +void IShaderProgramGenerator::OutputCubeFaceDepthGeometry(IShaderStageGenerator &geometryShader) +{ + geometryShader.Append("layout(triangles) in;"); + geometryShader.Append("layout(triangle_strip, max_vertices = 18) out;"); + // geometryShader.AddUniform("shadow_mvp[6]", "mat4"); + + geometryShader.AddUniform("shadow_mv0", "mat4"); + geometryShader.AddUniform("shadow_mv1", "mat4"); + geometryShader.AddUniform("shadow_mv2", "mat4"); + geometryShader.AddUniform("shadow_mv3", "mat4"); + geometryShader.AddUniform("shadow_mv4", "mat4"); + geometryShader.AddUniform("shadow_mv5", "mat4"); + geometryShader.AddUniform("projection", "mat4"); + + geometryShader.AddUniform("model_matrix", "mat4"); + geometryShader.AddOutgoing("world_pos", "vec4"); + + geometryShader.Append("void main() {"); + geometryShader.Append(" mat4 layerMVP[6];"); + geometryShader.Append(" layerMVP[0] = projection * shadow_mv0;"); + geometryShader.Append(" layerMVP[1] = projection * shadow_mv1;"); + geometryShader.Append(" layerMVP[2] = projection * shadow_mv2;"); + geometryShader.Append(" layerMVP[3] = projection * shadow_mv3;"); + geometryShader.Append(" layerMVP[4] = projection * shadow_mv4;"); + geometryShader.Append(" layerMVP[5] = projection * shadow_mv5;"); + geometryShader.Append(" for (int i = 0; i < 6; ++i)"); + geometryShader.Append(" {"); + geometryShader.Append(" gl_Layer = i;"); + geometryShader.Append(" for(int j = 0; j < 3; ++j)"); + geometryShader.Append(" {"); + geometryShader.Append(" world_pos = model_matrix * raw_pos[j];"); + geometryShader.Append(" world_pos /= world_pos.w;"); + geometryShader.Append(" gl_Position = layerMVP[j] * raw_pos[j];"); + geometryShader.Append(" world_pos.w = gl_Position.w;"); + geometryShader.Append(" EmitVertex();"); + geometryShader.Append(" }"); + geometryShader.Append(" EndPrimitive();"); + geometryShader.Append(" }"); + geometryShader.Append("}"); +} + +void IShaderProgramGenerator::OutputCubeFaceDepthFragment(IShaderStageGenerator &fragmentShader) +{ + fragmentShader.AddUniform("camera_position", "vec3"); + fragmentShader.AddUniform("camera_properties", "vec2"); + + fragmentShader.Append("void main() {"); + fragmentShader.Append( + "\tvec3 camPos = vec3( camera_position.x, camera_position.y, -camera_position.z );"); + fragmentShader.Append("\tfloat dist = length( world_pos.xyz - camPos );"); + fragmentShader.Append( + "\tdist = (dist - camera_properties.x) / (camera_properties.y - camera_properties.x);"); + // fragmentShader.Append("\tgl_FragDepth = dist;"); + fragmentShader.Append("\tfragOutput = vec4(dist, dist, dist, 1.0);"); + fragmentShader.Append("}"); +} diff --git a/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.h b/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.h new file mode 100644 index 0000000..6a6e967 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_CODE_GENERATOR_V2_H +#define QT3DS_RENDER_SHADER_CODE_GENERATOR_V2_H +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "Qt3DSRenderShaderCache.h" +#include "foundation/Qt3DSFlags.h" + +#include <QtCore/qstring.h> + +namespace qt3ds { +namespace render { + // So far the generator is only useful for graphics stages, + // it doesn't seem useful for compute stages. + struct ShaderGeneratorStages + { + enum Enum { + Vertex = 1, + TessControl = 1 << 1, + TessEval = 1 << 2, + Geometry = 1 << 3, + Fragment = 1 << 4, + StageCount = 5, + }; + }; + + typedef NVFlags<ShaderGeneratorStages::Enum, QT3DSU32> TShaderGeneratorStageFlags; + + class IShaderStageGenerator + { + protected: + virtual ~IShaderStageGenerator() {} + public: + virtual void AddIncoming(const char8_t *name, const char8_t *type) = 0; + virtual void AddIncoming(const TStrType &name, const char8_t *type) = 0; + virtual void AddIncoming(const QString &name, const char8_t *type) = 0; + + virtual void AddOutgoing(const char8_t *name, const char8_t *type) = 0; + virtual void AddOutgoing(const TStrType &name, const char8_t *type) = 0; + virtual void AddOutgoing(const QString &name, const char8_t *type) = 0; + + virtual void AddUniform(const char8_t *name, const char8_t *type) = 0; + virtual void AddUniform(const TStrType &name, const char8_t *type) = 0; + virtual void AddUniform(const QString &name, const char8_t *type) = 0; + + virtual void AddInclude(const char8_t *name) = 0; + virtual void AddInclude(const TStrType &name) = 0; + virtual void AddInclude(const QString &name) = 0; + + virtual void AddFunction(const QString &functionName) = 0; + + virtual void AddConstantBuffer(const char *name, const char *layout) = 0; + virtual void AddConstantBuffer(const QString &name, const char *layout) = 0; + virtual void AddConstantBufferParam(const QString &cbName, + const QString ¶mName, + const char *type) = 0; + virtual void AddConstantBufferParam(const char *cbName, const char *paramName, + const char *type) = 0; + + virtual IShaderStageGenerator &operator<<(const QString &data) = 0; + virtual IShaderStageGenerator &operator<<(const char *data) = 0; + virtual IShaderStageGenerator &operator<<(const TStrType &data) = 0; + virtual IShaderStageGenerator &operator<<(const SEndlType & /*data*/) = 0; + virtual void Append(const char *data) = 0; + virtual void AppendPartial(const char *data) = 0; + + virtual ShaderGeneratorStages::Enum Stage() const = 0; + }; + + class IShaderProgramGenerator : public NVRefCounted + { + public: + static TShaderGeneratorStageFlags DefaultFlags() + { + return TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex + | ShaderGeneratorStages::Fragment); + } + virtual void BeginProgram(TShaderGeneratorStageFlags inEnabledStages = DefaultFlags()) = 0; + + virtual TShaderGeneratorStageFlags GetEnabledStages() const = 0; + + // get the stage or NULL if it has not been created. + virtual IShaderStageGenerator *GetStage(ShaderGeneratorStages::Enum inStage) = 0; + + // Implicit call to end program. + virtual qt3ds::render::NVRenderShaderProgram * + CompileGeneratedShader(const char *inShaderName, const SShaderCacheProgramFlags &inFlags, + TShaderFeatureSet inFeatureSet, bool separableProgram = false) = 0; + + qt3ds::render::NVRenderShaderProgram *CompileGeneratedShader(const char *inShaderName, + bool separableProgram = false) + { + return CompileGeneratedShader(inShaderName, SShaderCacheProgramFlags(), + TShaderFeatureSet(), separableProgram); + } + + static IShaderProgramGenerator &CreateProgramGenerator(IQt3DSRenderContext &inContext); + + static void OutputParaboloidDepthVertex(IShaderStageGenerator &inGenerator); + // By convention, the local space result of the TE is stored in vec4 pos local variable. + // This function expects such state. + static void OutputParaboloidDepthTessEval(IShaderStageGenerator &inGenerator); + // Utilities shared among the various different systems. + static void OutputParaboloidDepthFragment(IShaderStageGenerator &inGenerator); + + static void OutputCubeFaceDepthVertex(IShaderStageGenerator &inGenerator); + static void OutputCubeFaceDepthGeometry(IShaderStageGenerator &inGenerator); + static void OutputCubeFaceDepthFragment(IShaderStageGenerator &inGenerator); + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderShaderKeys.h b/src/runtimerender/Qt3DSRenderShaderKeys.h new file mode 100644 index 0000000..5d02e41 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShaderKeys.h @@ -0,0 +1,802 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_KEY_H +#define QT3DS_RENDER_SHADER_KEY_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSDataRef.h" +#include "EASTL/string.h" +#include "foundation/StringConversionImpl.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderTessModeValues.h" +#include "render/Qt3DSRenderBaseTypes.h" + +namespace qt3ds { +namespace render { + // We have an ever expanding set of properties we like to hash into one or more 32 bit + // quantities. + // Furthermore we would like this set of properties to be convertable to string + // So the shader cache file itself is somewhat human readable/diagnosable. + // To do this we create a set of objects that act as properties to the master shader key. + // These objects are tallied in order to figure out their actual offset into the shader key's + // data store. They are also run through in order to create the string shader cache key. + + struct SShaderKeyPropertyBase + { + const char *m_Name; + QT3DSU32 m_Offset; + SShaderKeyPropertyBase(const char *name = "") + : m_Name(name) + , m_Offset(0) + { + } + QT3DSU32 GetOffset() const { return m_Offset; } + void SetOffset(QT3DSU32 of) { m_Offset = of; } + + template <QT3DSU32 TBitWidth> + QT3DSU32 GetMaskTemplate() const + { + QT3DSU32 bit = m_Offset % 32; + QT3DSU32 startValue = (1 << TBitWidth) - 1; + QT3DSU32 mask = startValue << bit; + return mask; + } + + QT3DSU32 GetIdx() const { return m_Offset / 32; } + protected: + void InternalToString(eastl::string &ioStr, const char *inBuffer) const + { + ioStr.append(m_Name); + ioStr.append("="); + ioStr.append(inBuffer); + } + + static void InternalToString(eastl::string &ioStr, const char *name, bool inValue) + { + if (inValue) { + ioStr.append(name); + ioStr.append("="); + ioStr.append(inValue ? "true" : "false"); + } + } + }; + + struct SShaderKeyBoolean : public SShaderKeyPropertyBase + { + enum { + BitWidth = 1, + }; + + SShaderKeyBoolean(const char *name = "") + : SShaderKeyPropertyBase(name) + { + } + + QT3DSU32 GetMask() const { return GetMaskTemplate<BitWidth>(); } + void SetValue(NVDataRef<QT3DSU32> inDataStore, bool inValue) const + { + QT3DSU32 idx = GetIdx(); + QT3DS_ASSERT(inDataStore.size() > idx); + QT3DSU32 mask = GetMask(); + QT3DSU32 &target = inDataStore[idx]; + if (inValue == true) { + target = target | mask; + } else { + mask = ~mask; + target = target & mask; + } + } + + bool GetValue(NVConstDataRef<QT3DSU32> inDataStore) const + { + QT3DSU32 idx = GetIdx(); + QT3DSU32 mask = GetMask(); + const QT3DSU32 &target = inDataStore[idx]; + return (target & mask) ? true : false; + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + bool isHigh = GetValue(inKeySet); + InternalToString(ioStr, m_Name, isHigh); + } + }; + + template <QT3DSU32 TBitWidth> + struct SShaderKeyUnsigned : public SShaderKeyPropertyBase + { + enum { + BitWidth = TBitWidth, + }; + SShaderKeyUnsigned(const char *name = "") + : SShaderKeyPropertyBase(name) + { + } + QT3DSU32 GetMask() const { return GetMaskTemplate<BitWidth>(); } + void SetValue(NVDataRef<QT3DSU32> inDataStore, QT3DSU32 inValue) const + { + QT3DSU32 startValue = (1 << TBitWidth) - 1; + // Ensure inValue is within range of bit width. + inValue = inValue & startValue; + QT3DSU32 bit = m_Offset % 32; + QT3DSU32 mask = GetMask(); + QT3DSU32 idx = GetIdx(); + inValue = inValue << bit; + QT3DSU32 &target = inDataStore[idx]; + // Get rid of existing value + QT3DSU32 inverseMask = ~mask; + target = target & inverseMask; + target = target | inValue; + } + + QT3DSU32 GetValue(NVConstDataRef<QT3DSU32> inDataStore) const + { + QT3DSU32 idx = GetIdx(); + QT3DSU32 bit = m_Offset % 32; + QT3DSU32 mask = GetMask(); + const QT3DSU32 &target = inDataStore[idx]; + + QT3DSU32 retval = target & mask; + retval = retval >> bit; + return retval; + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + QT3DSU32 value = GetValue(inKeySet); + char buf[64]; + StringConversion<QT3DSU32>().ToStr(value, toDataRef(buf, 64)); + InternalToString(ioStr, buf); + } + }; + + struct SShaderKeyTessellation : public SShaderKeyUnsigned<4> + { + enum TessellationBits { + noTessellation = 1 << 0, + linearTessellation = 1 << 1, + phongTessellation = 1 << 2, + npatchTessellation = 1 << 3 + }; + + SShaderKeyTessellation(const char *name = "") + : SShaderKeyUnsigned<4>(name) + { + } + + bool GetBitValue(TessellationBits swizzleBit, NVConstDataRef<QT3DSU32> inKeySet) const + { + return (GetValue(inKeySet) & swizzleBit) ? true : false; + } + + void SetBitValue(TessellationBits swizzleBit, bool inValue, NVDataRef<QT3DSU32> inKeySet) + { + QT3DSU32 theValue = GetValue(inKeySet); + QT3DSU32 mask = swizzleBit; + if (inValue) { + theValue = theValue | mask; + } else { + mask = ~mask; + theValue = theValue & mask; + } + SetValue(inKeySet, theValue); + } + + void SetTessellationMode(NVDataRef<QT3DSU32> inKeySet, TessModeValues::Enum tessellationMode, + bool val) + { + switch (tessellationMode) { + case TessModeValues::NoTess: + SetBitValue(noTessellation, val, inKeySet); + break; + case TessModeValues::TessLinear: + SetBitValue(linearTessellation, val, inKeySet); + break; + case TessModeValues::TessNPatch: + SetBitValue(npatchTessellation, val, inKeySet); + break; + case TessModeValues::TessPhong: + SetBitValue(phongTessellation, val, inKeySet); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + bool IsNoTessellation(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(noTessellation, inKeySet); + } + void SetNoTessellation(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(noTessellation, val, inKeySet); + } + + bool IsLinearTessellation(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(linearTessellation, inKeySet); + } + void SetLinearTessellation(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(linearTessellation, val, inKeySet); + } + + bool IsNPatchTessellation(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(npatchTessellation, inKeySet); + } + void SetNPatchTessellation(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(npatchTessellation, val, inKeySet); + } + + bool IsPhongTessellation(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(phongTessellation, inKeySet); + } + void SetPhongTessellation(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(phongTessellation, val, inKeySet); + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + ioStr.append(m_Name); + ioStr.append("={"); + InternalToString(ioStr, "noTessellation", IsNoTessellation(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "linearTessellation", IsLinearTessellation(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "npatchTessellation", IsNPatchTessellation(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "phongTessellation", IsPhongTessellation(inKeySet)); + ioStr.append("}"); + } + }; + + struct SShaderKeyTextureSwizzle : public SShaderKeyUnsigned<5> + { + enum TextureSwizzleBits { + noSwizzle = 1 << 0, + L8toR8 = 1 << 1, + A8toR8 = 1 << 2, + L8A8toRG8 = 1 << 3, + L16toR16 = 1 << 4 + }; + + SShaderKeyTextureSwizzle(const char *name = "") + : SShaderKeyUnsigned<5>(name) + { + } + + bool GetBitValue(TextureSwizzleBits swizzleBit, NVConstDataRef<QT3DSU32> inKeySet) const + { + return (GetValue(inKeySet) & swizzleBit) ? true : false; + } + + void SetBitValue(TextureSwizzleBits swizzleBit, bool inValue, NVDataRef<QT3DSU32> inKeySet) + { + QT3DSU32 theValue = GetValue(inKeySet); + QT3DSU32 mask = swizzleBit; + if (inValue) { + theValue = theValue | mask; + } else { + mask = ~mask; + theValue = theValue & mask; + } + SetValue(inKeySet, theValue); + } + + void SetSwizzleMode(NVDataRef<QT3DSU32> inKeySet, NVRenderTextureSwizzleMode::Enum swizzleMode, + bool val) + { + switch (swizzleMode) { + case NVRenderTextureSwizzleMode::NoSwizzle: + SetBitValue(noSwizzle, val, inKeySet); + break; + case NVRenderTextureSwizzleMode::L8toR8: + SetBitValue(L8toR8, val, inKeySet); + break; + case NVRenderTextureSwizzleMode::A8toR8: + SetBitValue(A8toR8, val, inKeySet); + break; + case NVRenderTextureSwizzleMode::L8A8toRG8: + SetBitValue(L8A8toRG8, val, inKeySet); + break; + case NVRenderTextureSwizzleMode::L16toR16: + SetBitValue(L16toR16, val, inKeySet); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + + bool IsNoSwizzled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(noSwizzle, inKeySet); + } + void SetNoSwizzled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(noSwizzle, val, inKeySet); + } + + bool IsL8Swizzled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(L8toR8, inKeySet); + } + void SetL8Swizzled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(L8toR8, val, inKeySet); + } + + bool IsA8Swizzled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(A8toR8, inKeySet); + } + void SetA8Swizzled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(A8toR8, val, inKeySet); + } + + bool IsL8A8Swizzled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(L8A8toRG8, inKeySet); + } + void SetL8A8Swizzled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(L8A8toRG8, val, inKeySet); + } + + bool IsL16Swizzled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(L16toR16, inKeySet); + } + void SetL16Swizzled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(L16toR16, val, inKeySet); + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + ioStr.append(m_Name); + ioStr.append("={"); + InternalToString(ioStr, "noswizzle", IsNoSwizzled(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "l8swizzle", IsL8Swizzled(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "a8swizzle", IsA8Swizzled(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "l8a8swizzle", IsL8A8Swizzled(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "l16swizzle", IsL16Swizzled(inKeySet)); + ioStr.append("}"); + } + }; + + struct SShaderKeyImageMap : public SShaderKeyUnsigned<5> + { + enum ImageMapBits { + Enabled = 1 << 0, + EnvMap = 1 << 1, + LightProbe = 1 << 2, + InvertUV = 1 << 3, + Premultiplied = 1 << 4, + }; + + SShaderKeyImageMap(const char *name = "") + : SShaderKeyUnsigned<5>(name) + { + } + + bool GetBitValue(ImageMapBits imageBit, NVConstDataRef<QT3DSU32> inKeySet) const + { + return (GetValue(inKeySet) & imageBit) ? true : false; + } + + void SetBitValue(ImageMapBits imageBit, bool inValue, NVDataRef<QT3DSU32> inKeySet) + { + QT3DSU32 theValue = GetValue(inKeySet); + QT3DSU32 mask = imageBit; + if (inValue) { + theValue = theValue | mask; + } else { + mask = ~mask; + theValue = theValue & mask; + } + SetValue(inKeySet, theValue); + } + + bool IsEnabled(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(Enabled, inKeySet); + } + void SetEnabled(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(Enabled, val, inKeySet); + } + + bool IsEnvMap(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(EnvMap, inKeySet); + } + void SetEnvMap(NVDataRef<QT3DSU32> inKeySet, bool val) { SetBitValue(EnvMap, val, inKeySet); } + + bool IsLightProbe(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(LightProbe, inKeySet); + } + void SetLightProbe(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(LightProbe, val, inKeySet); + } + + bool IsInvertUVMap(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(InvertUV, inKeySet); + } + void SetInvertUVMap(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(InvertUV, val, inKeySet); + } + + bool IsPremultiplied(NVConstDataRef<QT3DSU32> inKeySet) const + { + return GetBitValue(Premultiplied, inKeySet); + } + void SetPremultiplied(NVDataRef<QT3DSU32> inKeySet, bool val) + { + SetBitValue(Premultiplied, val, inKeySet); + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + ioStr.append(m_Name); + ioStr.append("={"); + InternalToString(ioStr, "enabled", IsEnabled(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "envMap", IsEnvMap(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "lightProbe", IsLightProbe(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "invertUV", IsInvertUVMap(inKeySet)); + ioStr.append(";"); + InternalToString(ioStr, "premultiplied", IsPremultiplied(inKeySet)); + ioStr.append("}"); + } + }; + + struct SShaderKeySpecularModel : SShaderKeyUnsigned<2> + { + SShaderKeySpecularModel(const char *name = "") + : SShaderKeyUnsigned<2>(name) + { + } + + void SetSpecularModel(NVDataRef<QT3DSU32> inKeySet, + qt3ds::render::DefaultMaterialSpecularModel::Enum inModel) + { + SetValue(inKeySet, (QT3DSU32)inModel); + } + + qt3ds::render::DefaultMaterialSpecularModel::Enum + GetSpecularModel(NVConstDataRef<QT3DSU32> inKeySet) const + { + return static_cast<qt3ds::render::DefaultMaterialSpecularModel::Enum>(GetValue(inKeySet)); + } + + void ToString(eastl::string &ioStr, NVConstDataRef<QT3DSU32> inKeySet) const + { + ioStr.append(m_Name); + ioStr.append("="); + switch (GetSpecularModel(inKeySet)) { + case DefaultMaterialSpecularModel::KGGX: + ioStr.append("KGGX"); + break; + case DefaultMaterialSpecularModel::KWard: + ioStr.append("KWard"); + break; + case DefaultMaterialSpecularModel::Default: + ioStr.append("Default"); + break; + } + ioStr.append(";"); + } + }; + + struct SShaderDefaultMaterialKeyProperties + { + enum { + LightCount = 7, + }; + enum ImageMapNames { + DiffuseMap0 = 0, + DiffuseMap1, + DiffuseMap2, + EmissiveMap, + EmissiveMap2, + SpecularMap, + OpacityMap, + BumpMap, + SpecularAmountMap, + NormalMap, + DisplacementMap, + TranslucencyMap, + LightmapIndirect, + LightmapRadiosity, + LightmapShadow, + RoughnessMap, + ImageMapCount + }; + + SShaderKeyBoolean m_HasLighting; + SShaderKeyBoolean m_HasIbl; + SShaderKeyUnsigned<3> m_LightCount; + SShaderKeyBoolean m_LightFlags[LightCount]; + SShaderKeyBoolean m_LightAreaFlags[LightCount]; + SShaderKeyBoolean m_LightShadowFlags[LightCount]; + SShaderKeyBoolean m_SpecularEnabled; + SShaderKeyBoolean m_FresnelEnabled; + SShaderKeyBoolean m_VertexColorsEnabled; + SShaderKeySpecularModel m_SpecularModel; + SShaderKeyImageMap m_ImageMaps[ImageMapCount]; + SShaderKeyTextureSwizzle m_TextureSwizzle[ImageMapCount]; + SShaderKeyTessellation m_TessellationMode; + SShaderKeyBoolean m_HasSkinning; + SShaderKeyBoolean m_WireframeMode; + + SShaderDefaultMaterialKeyProperties() + : m_HasLighting("hasLighting") + , m_HasIbl("hasIbl") + , m_LightCount("lightCount") + , m_SpecularEnabled("specularEnabled") + , m_FresnelEnabled("fresnelEnabled") + , m_VertexColorsEnabled("vertexColorsEnabled") + , m_SpecularModel("specularModel") + , m_TessellationMode("tessellationMode") + , m_HasSkinning("hasSkinning") + , m_WireframeMode("wireframeMode") + { + m_LightFlags[0].m_Name = "light0HasPosition"; + m_LightFlags[1].m_Name = "light1HasPosition"; + m_LightFlags[2].m_Name = "light2HasPosition"; + m_LightFlags[3].m_Name = "light3HasPosition"; + m_LightFlags[4].m_Name = "light4HasPosition"; + m_LightFlags[5].m_Name = "light5HasPosition"; + m_LightFlags[6].m_Name = "light6HasPosition"; + m_LightAreaFlags[0].m_Name = "light0HasArea"; + m_LightAreaFlags[1].m_Name = "light1HasArea"; + m_LightAreaFlags[2].m_Name = "light2HasArea"; + m_LightAreaFlags[3].m_Name = "light3HasArea"; + m_LightAreaFlags[4].m_Name = "light4HasArea"; + m_LightAreaFlags[5].m_Name = "light5HasArea"; + m_LightAreaFlags[6].m_Name = "light6HasArea"; + m_LightShadowFlags[0].m_Name = "light0HasShadow"; + m_LightShadowFlags[1].m_Name = "light1HasShadow"; + m_LightShadowFlags[2].m_Name = "light2HasShadow"; + m_LightShadowFlags[3].m_Name = "light3HasShadow"; + m_LightShadowFlags[4].m_Name = "light4HasShadow"; + m_LightShadowFlags[5].m_Name = "light5HasShadow"; + m_LightShadowFlags[6].m_Name = "light6HasShadow"; + m_ImageMaps[0].m_Name = "diffuseMap0"; + m_ImageMaps[1].m_Name = "diffuseMap1"; + m_ImageMaps[2].m_Name = "diffuseMap2"; + m_ImageMaps[3].m_Name = "emissiveMap"; + m_ImageMaps[4].m_Name = "emissiveMap2"; + m_ImageMaps[5].m_Name = "specularMap"; + m_ImageMaps[6].m_Name = "opacityMap"; + m_ImageMaps[7].m_Name = "bumpMap"; + m_ImageMaps[8].m_Name = "specularAmountMap"; + m_ImageMaps[9].m_Name = "normalMap"; + m_ImageMaps[10].m_Name = "displacementMap"; + m_ImageMaps[11].m_Name = "translucencyMap"; + m_ImageMaps[12].m_Name = "lightmapIndirect"; + m_ImageMaps[13].m_Name = "lightmapRadiosity"; + m_ImageMaps[14].m_Name = "lightmapShadow"; + m_ImageMaps[15].m_Name = "roughnessMap"; + m_TextureSwizzle[0].m_Name = "diffuseMap0_swizzle"; + m_TextureSwizzle[1].m_Name = "diffuseMap1_swizzle"; + m_TextureSwizzle[2].m_Name = "diffuseMap2_swizzle"; + m_TextureSwizzle[3].m_Name = "emissiveMap_swizzle"; + m_TextureSwizzle[4].m_Name = "emissiveMap2_swizzle"; + m_TextureSwizzle[5].m_Name = "specularMap_swizzle"; + m_TextureSwizzle[6].m_Name = "opacityMap_swizzle"; + m_TextureSwizzle[7].m_Name = "bumpMap_swizzle"; + m_TextureSwizzle[8].m_Name = "specularAmountMap_swizzle"; + m_TextureSwizzle[9].m_Name = "normalMap_swizzle"; + m_TextureSwizzle[10].m_Name = "displacementMap_swizzle"; + m_TextureSwizzle[11].m_Name = "translucencyMap_swizzle"; + m_TextureSwizzle[12].m_Name = "lightmapIndirect_swizzle"; + m_TextureSwizzle[13].m_Name = "lightmapRadiosity_swizzle"; + m_TextureSwizzle[14].m_Name = "lightmapShadow_swizzle"; + m_TextureSwizzle[15].m_Name = "roughnessMap_swizzle"; + SetPropertyOffsets(); + } + + template <typename TVisitor> + void VisitProperties(TVisitor &inVisitor) + { + inVisitor.Visit(m_HasLighting); + inVisitor.Visit(m_HasIbl); + inVisitor.Visit(m_LightCount); + + for (QT3DSU32 idx = 0, end = LightCount; idx < end; ++idx) { + inVisitor.Visit(m_LightFlags[idx]); + } + + for (QT3DSU32 idx = 0, end = LightCount; idx < end; ++idx) { + inVisitor.Visit(m_LightAreaFlags[idx]); + } + + for (QT3DSU32 idx = 0, end = LightCount; idx < end; ++idx) { + inVisitor.Visit(m_LightShadowFlags[idx]); + } + + inVisitor.Visit(m_SpecularEnabled); + inVisitor.Visit(m_FresnelEnabled); + inVisitor.Visit(m_VertexColorsEnabled); + inVisitor.Visit(m_SpecularModel); + + for (QT3DSU32 idx = 0, end = ImageMapCount; idx < end; ++idx) { + inVisitor.Visit(m_ImageMaps[idx]); + inVisitor.Visit(m_TextureSwizzle[idx]); + } + + inVisitor.Visit(m_TessellationMode); + inVisitor.Visit(m_HasSkinning); + inVisitor.Visit(m_WireframeMode); + } + + struct SOffsetVisitor + { + QT3DSU32 m_Offset; + SOffsetVisitor() + : m_Offset(0) + { + } + template <typename TPropType> + void Visit(TPropType &inProp) + { + // if we cross the 32 bit border we just move + // to the next dword. + // This cost a few extra bits but prevents tedious errors like + // loosing shader key bits because they got moved beyond the 32 border + QT3DSU32 bit = m_Offset % 32; + if (bit + TPropType::BitWidth > 31) { + m_Offset += 32 - bit; + } + + inProp.SetOffset(m_Offset); + m_Offset += TPropType::BitWidth; + } + }; + + void SetPropertyOffsets() + { + SOffsetVisitor visitor; + VisitProperties(visitor); + // If this assert fires, then the default material key needs more bits. + QT3DS_ASSERT(visitor.m_Offset < 224); + } + }; + + struct SShaderDefaultMaterialKey + { + enum { + DataBufferSize = 7, + }; + QT3DSU32 m_DataBuffer[DataBufferSize]; + size_t m_FeatureSetHash; + + SShaderDefaultMaterialKey(size_t inFeatureSetHash) + : m_FeatureSetHash(inFeatureSetHash) + { + for (size_t idx = 0; idx < DataBufferSize; ++idx) + m_DataBuffer[idx] = 0; + } + + SShaderDefaultMaterialKey() + : m_FeatureSetHash(0) + { + for (size_t idx = 0; idx < DataBufferSize; ++idx) + m_DataBuffer[idx] = 0; + } + + size_t hash() const + { + size_t retval = 0; + for (size_t idx = 0; idx < DataBufferSize; ++idx) + retval = retval ^ eastl::hash<QT3DSU32>()(m_DataBuffer[idx]); + return retval ^ m_FeatureSetHash; + } + + bool operator==(const SShaderDefaultMaterialKey &other) const + { + bool retval = true; + for (size_t idx = 0; idx < DataBufferSize && retval; ++idx) + retval = m_DataBuffer[idx] == other.m_DataBuffer[idx]; + return retval && m_FeatureSetHash == other.m_FeatureSetHash; + } + + // Cast operators to make getting properties easier. + operator NVDataRef<QT3DSU32>() { return toDataRef(m_DataBuffer, DataBufferSize); } + + operator NVConstDataRef<QT3DSU32>() const + { + return toConstDataRef(m_DataBuffer, DataBufferSize); + } + + struct SStringVisitor + { + eastl::string &m_Str; + NVConstDataRef<QT3DSU32> m_KeyStore; + SStringVisitor(eastl::string &s, NVConstDataRef<QT3DSU32> ks) + : m_Str(s) + , m_KeyStore(ks) + { + } + template <typename TPropType> + void Visit(const TPropType &prop) + { + QT3DSU32 originalSize = m_Str.size(); + if (m_Str.size()) + m_Str.append(";"); + prop.ToString(m_Str, m_KeyStore); + // if the only thing we added was the semicolon + // then nuke the semicolon + if (originalSize && m_Str.size() == originalSize + 1) + m_Str.resize(originalSize); + } + }; + + void ToString(eastl::string &ioString, + SShaderDefaultMaterialKeyProperties &inProperties) const + { + SStringVisitor theVisitor(ioString, *this); + inProperties.VisitProperties(theVisitor); + } + }; +} +} + +namespace eastl { +template <> +struct hash<qt3ds::render::SShaderDefaultMaterialKey> +{ + size_t operator()(const qt3ds::render::SShaderDefaultMaterialKey &key) const + { + return key.hash(); + } +}; +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderShadowMap.cpp b/src/runtimerender/Qt3DSRenderShadowMap.cpp new file mode 100644 index 0000000..983af55 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShadowMap.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLayer.h" +#include "Qt3DSRenderShadowMap.h" +#include "Qt3DSRenderResourceManager.h" +#include "rendererimpl/Qt3DSRendererImplLayerRenderData.h" +#include "render/Qt3DSRenderShaderConstant.h" +#include "render/Qt3DSRenderShaderProgram.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderContextScopedProperty; +using qt3ds::render::NVRenderCachedShaderProperty; + +Qt3DSShadowMap::Qt3DSShadowMap(IQt3DSRenderContext &inContext) + : m_Context(inContext) + , mRefCount(0) + , m_ShadowMapList(inContext.GetAllocator(), "Qt3DSShadowMap::m_ShadowMapList") +{ +} + +Qt3DSShadowMap::~Qt3DSShadowMap() +{ + m_ShadowMapList.clear(); +} + +namespace { +bool IsDepthFormat(NVRenderTextureFormats::Enum format) +{ + switch (format) { + case NVRenderTextureFormats::Depth16: + case NVRenderTextureFormats::Depth24: + case NVRenderTextureFormats::Depth32: + case NVRenderTextureFormats::Depth24Stencil8: + return true; + default: + return false; + } +} +} + +void Qt3DSShadowMap::AddShadowMapEntry(QT3DSU32 index, QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum format, QT3DSU32 samples, + ShadowMapModes::Enum mode, ShadowFilterValues::Enum filter) +{ + IResourceManager &theManager(m_Context.GetResourceManager()); + SShadowMapEntry *pEntry = NULL; + + if (index < m_ShadowMapList.size()) + pEntry = &m_ShadowMapList[index]; + + if (pEntry) { + if ((NULL != pEntry->m_DepthMap) && (mode == ShadowMapModes::CUBE)) { + theManager.Release(*pEntry->m_DepthMap); + theManager.Release(*pEntry->m_DepthCopy); + theManager.Release(*pEntry->m_DepthRender); + pEntry->m_DepthCube = theManager.AllocateTextureCube(width, height, format, samples); + pEntry->m_CubeCopy = theManager.AllocateTextureCube(width, height, format, samples); + pEntry->m_DepthRender = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + pEntry->m_DepthMap = NULL; + pEntry->m_DepthCopy = NULL; + } else if ((NULL != pEntry->m_DepthCube) && (mode != ShadowMapModes::CUBE)) { + theManager.Release(*pEntry->m_DepthCube); + theManager.Release(*pEntry->m_CubeCopy); + theManager.Release(*pEntry->m_DepthRender); + pEntry->m_DepthMap = theManager.AllocateTexture2D(width, height, format, samples); + pEntry->m_DepthCopy = theManager.AllocateTexture2D(width, height, format, samples); + pEntry->m_DepthCube = NULL; + pEntry->m_CubeCopy = NULL; + pEntry->m_DepthRender = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + } else if (NULL != pEntry->m_DepthMap) { + STextureDetails theDetails(pEntry->m_DepthMap->GetTextureDetails()); + + // If anything differs about the map we're looking for, let's recreate it. + if (theDetails.m_Format != format || theDetails.m_Width != width + || theDetails.m_Height != height || theDetails.m_SampleCount != samples) { + // release texture + theManager.Release(*pEntry->m_DepthMap); + theManager.Release(*pEntry->m_DepthCopy); + theManager.Release(*pEntry->m_DepthRender); + pEntry->m_DepthMap = theManager.AllocateTexture2D(width, height, format, samples); + pEntry->m_DepthCopy = theManager.AllocateTexture2D(width, height, format, samples); + pEntry->m_DepthCube = NULL; + pEntry->m_CubeCopy = NULL; + pEntry->m_DepthRender = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + } + } else { + STextureDetails theDetails(pEntry->m_DepthCube->GetTextureDetails()); + + // If anything differs about the map we're looking for, let's recreate it. + if (theDetails.m_Format != format || theDetails.m_Width != width + || theDetails.m_Height != height || theDetails.m_SampleCount != samples) { + // release texture + theManager.Release(*pEntry->m_DepthCube); + theManager.Release(*pEntry->m_CubeCopy); + theManager.Release(*pEntry->m_DepthRender); + pEntry->m_DepthCube = + theManager.AllocateTextureCube(width, height, format, samples); + pEntry->m_CubeCopy = theManager.AllocateTextureCube(width, height, format, samples); + pEntry->m_DepthRender = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + pEntry->m_DepthMap = NULL; + pEntry->m_DepthCopy = NULL; + } + } + + pEntry->m_ShadowMapMode = mode; + pEntry->m_ShadowFilterFlags = filter; + } else if (mode == ShadowMapModes::CUBE) { + NVRenderTextureCube *theDepthTex = + theManager.AllocateTextureCube(width, height, format, samples); + NVRenderTextureCube *theDepthCopy = + theManager.AllocateTextureCube(width, height, format, samples); + NVRenderTexture2D *theDepthTemp = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + + m_ShadowMapList.push_back( + SShadowMapEntry(index, mode, filter, *theDepthTex, *theDepthCopy, *theDepthTemp)); + + pEntry = &m_ShadowMapList.back(); + } else { + NVRenderTexture2D *theDepthMap = + theManager.AllocateTexture2D(width, height, format, samples); + NVRenderTexture2D *theDepthCopy = + theManager.AllocateTexture2D(width, height, format, samples); + NVRenderTexture2D *theDepthTemp = theManager.AllocateTexture2D( + width, height, NVRenderTextureFormats::Depth24Stencil8, samples); + + m_ShadowMapList.push_back( + SShadowMapEntry(index, mode, filter, *theDepthMap, *theDepthCopy, *theDepthTemp)); + + pEntry = &m_ShadowMapList.back(); + } + + if (pEntry) { + // setup some texture settings + if (pEntry->m_DepthMap) { + pEntry->m_DepthMap->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_DepthMap->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_DepthMap->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_DepthMap->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + + pEntry->m_DepthCopy->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_DepthCopy->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_DepthCopy->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_DepthCopy->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + + pEntry->m_DepthRender->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_DepthRender->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_DepthRender->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_DepthRender->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + } else { + pEntry->m_DepthCube->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_DepthCube->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_DepthCube->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_DepthCube->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + + pEntry->m_CubeCopy->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_CubeCopy->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_CubeCopy->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_CubeCopy->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + + pEntry->m_DepthRender->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + pEntry->m_DepthRender->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + pEntry->m_DepthRender->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + pEntry->m_DepthRender->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + } + + pEntry->m_LightIndex = index; + } +} + +SShadowMapEntry *Qt3DSShadowMap::GetShadowMapEntry(QT3DSU32 index) +{ + SShadowMapEntry *pEntry = NULL; + + for (QT3DSU32 i = 0; i < m_ShadowMapList.size(); i++) { + pEntry = &m_ShadowMapList[i]; + if (pEntry->m_LightIndex == index) + return pEntry; + } + + return NULL; +} + +Qt3DSShadowMap *Qt3DSShadowMap::Create(IQt3DSRenderContext &inContext) +{ + return QT3DS_NEW(inContext.GetFoundation().getAllocator(), Qt3DSShadowMap)(inContext); +} diff --git a/src/runtimerender/Qt3DSRenderShadowMap.h b/src/runtimerender/Qt3DSRenderShadowMap.h new file mode 100644 index 0000000..c5ba2ea --- /dev/null +++ b/src/runtimerender/Qt3DSRenderShadowMap.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADOW_MAP_H +#define QT3DS_RENDER_SHADOW_MAP_H +#include "Qt3DSRenderContextCore.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSMat44.h" +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "render/Qt3DSRenderTexture2D.h" +#ifdef _INTEGRITYPLATFORM +#include "render/Qt3DSRenderTextureCube.h" +#endif + +namespace qt3ds { +namespace render { + + struct SLayerRenderData; + + struct ShadowMapModes + { + enum Enum { + SSM, ///< standard shadow mapping + VSM, ///< variance shadow mapping + CUBE, ///< cubemap omnidirectional shadows + }; + }; + + struct ShadowFilterValues + { + enum Enum { + NONE = 1 << 0, ///< hard shadows + PCF = 1 << 1, ///< Percentage close filtering + BLUR = 1 << 2, ///< Gausian Blur + }; + }; + + struct SShadowMapEntry + { + SShadowMapEntry() + : m_LightIndex(QT3DS_MAX_U32) + , m_ShadowMapMode(ShadowMapModes::SSM) + , m_ShadowFilterFlags(ShadowFilterValues::NONE) + { + } + + SShadowMapEntry(QT3DSU32 index, ShadowMapModes::Enum mode, ShadowFilterValues::Enum filter, + NVRenderTexture2D &depthMap, NVRenderTexture2D &depthCopy, + NVRenderTexture2D &depthTemp) + : m_LightIndex(index) + , m_ShadowMapMode(mode) + , m_ShadowFilterFlags(filter) + , m_DepthMap(depthMap) + , m_DepthCopy(depthCopy) + , m_DepthCube(NULL) + , m_CubeCopy(NULL) + , m_DepthRender(depthTemp) + { + } + + SShadowMapEntry(QT3DSU32 index, ShadowMapModes::Enum mode, ShadowFilterValues::Enum filter, + NVRenderTextureCube &depthCube, NVRenderTextureCube &cubeTmp, + NVRenderTexture2D &depthTemp) + : m_LightIndex(index) + , m_ShadowMapMode(mode) + , m_ShadowFilterFlags(filter) + , m_DepthMap(NULL) + , m_DepthCopy(NULL) + , m_DepthCube(depthCube) + , m_CubeCopy(cubeTmp) + , m_DepthRender(depthTemp) + { + } + + QT3DSU32 m_LightIndex; ///< the light index it belongs to + ShadowMapModes::Enum m_ShadowMapMode; ///< shadow map method + ShadowFilterValues::Enum m_ShadowFilterFlags; ///< shadow filter mode + + // PKC : Adding the DepthRender buffer allows us to have a depth+stencil format when filling + // the shadow maps (depth+stencil is necessary), but use a more compact format for the + // actual + // shadow map used at shade time. See if it's worth adding. + NVScopedRefCounted<NVRenderTexture2D> m_DepthMap; ///< shadow map texture + NVScopedRefCounted<NVRenderTexture2D> + m_DepthCopy; ///< shadow map buffer used during blur passes + NVScopedRefCounted<NVRenderTextureCube> m_DepthCube; ///< shadow cube map + NVScopedRefCounted<NVRenderTextureCube> + m_CubeCopy; ///< cube map buffer used during the blur passes + NVScopedRefCounted<NVRenderTexture2D> + m_DepthRender; ///< shadow depth+stencil map used during rendering + + QT3DSMat44 m_LightVP; ///< light view projection matrix + QT3DSMat44 m_LightCubeView[6]; ///< light cubemap view matrices + QT3DSMat44 m_LightView; ///< light view transform + }; + + class Qt3DSShadowMap : public NVRefCounted + { + typedef nvvector<SShadowMapEntry> TShadowMapEntryList; + + public: + IQt3DSRenderContext &m_Context; + volatile QT3DSI32 mRefCount; + + public: + Qt3DSShadowMap(IQt3DSRenderContext &inContext); + ~Qt3DSShadowMap(); + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Context.GetAllocator()) + + /* + * @brief Add a shadow map entry + * This creates a new shadow map if it does not exist or changed + * + * @param[in] index shadow map entry index + * @param[in] width shadow map width + * @param[in] height shadow map height + * @param[in] format shadow map format + * @param[in] samples shadow map sample count + * @param[in] mode shadow map mode like SSM, VCM + * @param[in] filter soft shadow map mode filter like PCF + * + * @ return no return + */ + void AddShadowMapEntry(QT3DSU32 index, QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum format, QT3DSU32 samples, + ShadowMapModes::Enum mode, ShadowFilterValues::Enum filter); + + /* + * @brief Get a shadow map entry + * + * @param[in] index shadow map entry index + * + * @ return shadow map entry or NULL + */ + SShadowMapEntry *GetShadowMapEntry(QT3DSU32 index); + + /* + * @brief Get shadow map entry count + * + * @ return count of shadow map entries + */ + QT3DSU32 GetShadowMapEntryCount() { return m_ShadowMapList.size(); } + + static Qt3DSShadowMap *Create(IQt3DSRenderContext &inContext); + + private: + TShadowMapEntryList m_ShadowMapList; ///< List of shadow map entries + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderSubPresentationHelper.h b/src/runtimerender/Qt3DSRenderSubPresentationHelper.h new file mode 100644 index 0000000..d77c7a7 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderSubPresentationHelper.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SUB_PRESENTATION_HELPER_H +#define QT3DS_RENDER_SUB_PRESENTATION_HELPER_H +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderer.h" +#include "render/Qt3DSRenderContext.h" + +namespace qt3ds { +namespace render { + + // Small helper object to setup the state needed to render a sub presentation + // correctly. Sub presentations may have transparency, and if they do then + // then need to be rendered with pre multiple alpha disabled. If they don't, + // then they need to be rendered with pre-multiply alpha enabled (and have the alpha channel + // set to 1 + struct SSubPresentationHelper + { + IQt3DSRenderContext &m_RenderContext; + QSize m_PreviousPresentationDimensions; + + bool m_WasInSubPresentation; + + SSubPresentationHelper(IQt3DSRenderContext &inContext, + const QSize &inPresDimensions) + : m_RenderContext(inContext) + , m_PreviousPresentationDimensions(inContext.GetCurrentPresentationDimensions()) + , m_WasInSubPresentation(inContext.IsInSubPresentation()) + { + m_RenderContext.SetInSubPresentation(true); + m_RenderContext.SetPresentationDimensions(inPresDimensions); + } + ~SSubPresentationHelper() + { + m_RenderContext.SetInSubPresentation(m_WasInSubPresentation); + m_RenderContext.SetPresentationDimensions(m_PreviousPresentationDimensions); + } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderSubpresentation.cpp b/src/runtimerender/Qt3DSRenderSubpresentation.cpp new file mode 100644 index 0000000..0b9d42d --- /dev/null +++ b/src/runtimerender/Qt3DSRenderSubpresentation.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderSubpresentation.h" +#include "Qt3DSRenderRenderList.h" +#ifdef _WIN32 +#pragma warning(disable : 4355) // this used in initializer list. I have never seen this result in + // a physical error +#endif +namespace qt3ds { +namespace render { + + Qt3DSRenderPickResult CSubPresentationPickQuery::Pick(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool inPickEverything) + { + return m_Renderer.DoGraphQueryPick(inMouseCoords, inViewportDimensions, inPickEverything); + } + + CSubPresentationRenderer::CSubPresentationRenderer(IQt3DSRenderContext &inRenderContext, + SPresentation &inPresentation) + : m_RenderContext(inRenderContext) + , m_Presentation(inPresentation) + , mRefCount(0) + , m_PickQuery(*this) + , m_OffscreenRendererType(inRenderContext.GetStringTable().RegisterStr(GetRendererName())) + { + } + + SOffscreenRendererEnvironment + CSubPresentationRenderer::GetDesiredEnvironment(QT3DSVec2 /*inPresScale*/) + { + // If we aren't using a clear color, then we are expected to blend with the background + bool hasTransparency = m_Presentation.m_Scene->m_UseClearColor ? false : true; + NVRenderTextureFormats::Enum format = + hasTransparency ? NVRenderTextureFormats::RGBA8 : NVRenderTextureFormats::RGB8; + return SOffscreenRendererEnvironment((QT3DSU32)(m_Presentation.m_PresentationDimensions.x), + (QT3DSU32)(m_Presentation.m_PresentationDimensions.y), + format, OffscreenRendererDepthValues::Depth16, false, + AAModeValues::NoAA); + } + + SOffscreenRenderFlags + CSubPresentationRenderer::NeedsRender(const SOffscreenRendererEnvironment & /*inEnvironment*/, + QT3DSVec2 /*inPresScale*/, + const SRenderInstanceId instanceId) + { + bool hasTransparency = m_Presentation.m_Scene->m_UseClearColor ? false : true; + NVRenderRect theViewportSize(m_RenderContext.GetRenderList().GetViewport()); + bool wasDirty = m_Presentation.m_Scene->PrepareForRender( + QT3DSVec2((QT3DSF32)theViewportSize.m_Width, (QT3DSF32)theViewportSize.m_Height), + m_RenderContext, instanceId); + return SOffscreenRenderFlags(hasTransparency, wasDirty); + } + + // Returns true if the rendered result image has transparency, or false + // if it should be treated as a completely opaque image. + void CSubPresentationRenderer::Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2, + SScene::RenderClearCommand inClearColorBuffer, + const SRenderInstanceId instanceId) + { + SSubPresentationHelper theHelper( + m_RenderContext, + QSize((QT3DSU32)inEnvironment.m_Width, (QT3DSU32)inEnvironment.m_Height)); + NVRenderRect theViewportSize(inRenderContext.GetViewport()); + m_Presentation.m_Scene->Render( + QT3DSVec2((QT3DSF32)theViewportSize.m_Width, (QT3DSF32)theViewportSize.m_Height), + m_RenderContext, inClearColorBuffer, instanceId); + m_LastRenderedEnvironment = inEnvironment; + } + + void CSubPresentationRenderer::RenderWithClear( + const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, + SScene::RenderClearCommand inClearBuffer, QT3DSVec4 inClearColor, + const SRenderInstanceId id) + { + Q_UNUSED(inEnvironment); + Q_UNUSED(inPresScale); + NVRenderRect theViewportSize(inRenderContext.GetViewport()); + m_Presentation.m_Scene->RenderWithClear( + QT3DSVec2((QT3DSF32)theViewportSize.m_Width, (QT3DSF32)theViewportSize.m_Height), + m_RenderContext, inClearBuffer, inClearColor, id); + } + + // You know the viewport dimensions because + Qt3DSRenderPickResult CSubPresentationRenderer::DoGraphQueryPick( + const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inViewportDimensions, bool inPickEverything) + { + Qt3DSRenderPickResult thePickResult; + + if (m_Presentation.m_Scene && m_Presentation.m_Scene->m_FirstChild) { + thePickResult = m_RenderContext.GetRenderer().Pick( + *m_Presentation.m_Scene->m_FirstChild, inViewportDimensions, + QT3DSVec2(inMouseCoords.x, inMouseCoords.y), true, inPickEverything); + } + return thePickResult; + } +} +} diff --git a/src/runtimerender/Qt3DSRenderSubpresentation.h b/src/runtimerender/Qt3DSRenderSubpresentation.h new file mode 100644 index 0000000..4adac84 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderSubpresentation.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SUBPRESENTATION_H +#define QT3DS_RENDER_SUBPRESENTATION_H + +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderSubPresentationHelper.h" +#include "Qt3DSRenderPresentation.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderBaseTypes.h" + +namespace qt3ds { +namespace render { + + class CSubPresentationRenderer; + + struct CSubPresentationPickQuery : public IGraphObjectPickQuery + { + CSubPresentationRenderer &m_Renderer; + + CSubPresentationPickQuery(CSubPresentationRenderer &renderer) + : m_Renderer(renderer) + { + } + Qt3DSRenderPickResult Pick(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool inPickEverything) override; + }; + + class CSubPresentationRenderer : public IOffscreenRenderer + { + public: + IQt3DSRenderContext &m_RenderContext; + SPresentation &m_Presentation; + volatile QT3DSI32 mRefCount; + SOffscreenRendererEnvironment m_LastRenderedEnvironment; + CSubPresentationPickQuery m_PickQuery; + CRegisteredString m_OffscreenRendererType; + + CSubPresentationRenderer(IQt3DSRenderContext &inRenderContext, SPresentation &inPresentation); + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) + + SOffscreenRendererEnvironment GetDesiredEnvironment(QT3DSVec2 inPresScale) override; + virtual SOffscreenRenderFlags + NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, QT3DSVec2 inPresScale, + const SRenderInstanceId instanceId) override; + void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext & /*inRenderContext*/, + QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, + const SRenderInstanceId instanceId) override; + void RenderWithClear(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, + QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, + QT3DSVec4 inClearColor, + const SRenderInstanceId instanceId) override; + IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId) override { return &m_PickQuery; } + bool Pick(const QT3DSVec2 & /*inMouseCoords*/, const QT3DSVec2 & /*inViewportDimensions*/, + const SRenderInstanceId) override + { + return false; + } + void addCallback(IOffscreenRendererCallback *cb) override + { + + } + // Used for RTTI purposes so we can safely static-cast an offscreen renderer to a + // CSubPresentationRenderer + static const char *GetRendererName() { return "SubPresentation"; } + CRegisteredString GetOffscreenRendererType() override { return m_OffscreenRendererType; } + + Qt3DSRenderPickResult DoGraphQueryPick(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool inPickEverything); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderTaggedPointer.h b/src/runtimerender/Qt3DSRenderTaggedPointer.h new file mode 100644 index 0000000..8dd8bdd --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTaggedPointer.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TAGGED_POINTER_H +#define QT3DS_RENDER_TAGGED_POINTER_H +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace render { + + // User's will need to define specialize this struct in order + // to de-tag a pointer. + template <typename TDataType> + struct SPointerTag + { + /* Expected API for runtime RTTI + static CRegisteredString GetTag() { return g_dtype_specific_string; } + */ + }; + + // A pointer tagged with an identifier so we can have generic + // user data that is still somewhat typesafe. + struct STaggedPointer + { + void *m_UserData; + QT3DSU32 m_Tag; + STaggedPointer() + : m_UserData(NULL) + , m_Tag(0) + { + } + + STaggedPointer(void *inUserData, QT3DSU32 inTag) + : m_UserData(inUserData) + , m_Tag(inTag) + { + } + + template <typename TDataType> + STaggedPointer(TDataType *inType) + : m_UserData(reinterpret_cast<void *>(inType)) + , m_Tag(SPointerTag<TDataType>::GetTag()) + { + } + + template <typename TDataType> + TDataType *DynamicCast() const + { + if (m_Tag == SPointerTag<TDataType>::GetTag()) + return reinterpret_cast<TDataType *>(m_UserData); + return NULL; + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderTessModeValues.h b/src/runtimerender/Qt3DSRenderTessModeValues.h new file mode 100644 index 0000000..15ea03c --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTessModeValues.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TESS_MODE_VALUES_H +#define QT3DS_RENDER_TESS_MODE_VALUES_H +#include "Qt3DSRender.h" + +namespace qt3ds { +namespace render { + + struct SDefaultMaterial; + class IBufferManager; + + struct TessModeValues + { + enum Enum { + NoTess = 0, + TessLinear = 1, + TessPhong = 2, + TessNPatch = 3, + }; + + static const char *toString(Enum value) + { + switch (value) { + case NoTess: + return "NoTess"; + break; + case TessLinear: + return "TessLinear"; + break; + case TessPhong: + return "TessPhong"; + break; + case TessNPatch: + return "TessNPatch"; + break; + default: + return "NoTess"; + break; + } + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSRenderTextTextureAtlas.cpp b/src/runtimerender/Qt3DSRenderTextTextureAtlas.cpp new file mode 100644 index 0000000..81c1d23 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextTextureAtlas.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSTextRenderer.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "render/Qt3DSRenderContext.h" + +using namespace qt3ds::render; + +namespace { + +struct STextTextureAtlas : public ITextTextureAtlas +{ + static const QT3DSI32 TEXTURE_ATLAS_DIM = + 256; // if you change this you need to adjust Qt3DSOnscreenTextRenderer size as well + + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + NVScopedRefCounted<ITextRenderer> m_TextRenderer; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + + STextTextureAtlas(NVFoundationBase &inFnd, ITextRenderer &inRenderer, + NVRenderContext &inRenderContext) + : m_Foundation(inFnd) + , mRefCount(0) + , m_TextRenderer(inRenderer) + , m_RenderContext(inRenderContext) + , m_TextureAtlasInitialized(false) + , m_textureAtlas(NULL) + { + } + + virtual ~STextTextureAtlas() + { + if (m_textureAtlas) { + m_textureAtlas->release(); + } + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + TTextRenderAtlasDetailsAndTexture RenderText(const STextRenderInfo &inText) override + { + SRenderTextureAtlasDetails theDetails = m_TextRenderer->RenderText(inText); + + return TTextRenderAtlasDetailsAndTexture(theDetails, m_textureAtlas); + } + + bool IsInitialized() override { return m_TextureAtlasInitialized && m_textureAtlas; } + + TTextTextureAtlasDetailsAndTexture PrepareTextureAtlas() override + { + if (!m_TextureAtlasInitialized && !m_textureAtlas) { + // create the texture atlas entries + QT3DSI32 count = m_TextRenderer->CreateTextureAtlas(); + + m_textureAtlas = m_RenderContext->CreateTexture2D(); + if (m_textureAtlas && count) { + m_TextureAtlasInitialized = true; + m_textureAtlas->addRef(); + // if you change the size you need to adjust Qt3DSOnscreenTextRenderer too + if (m_RenderContext->GetRenderContextType() == NVRenderContextValues::GLES2) { + m_textureAtlas->SetTextureData(NVDataRef<QT3DSU8>(), 0, TEXTURE_ATLAS_DIM, + TEXTURE_ATLAS_DIM, NVRenderTextureFormats::RGBA8); + } else { + m_textureAtlas->SetTextureData(NVDataRef<QT3DSU8>(), 0, TEXTURE_ATLAS_DIM, + TEXTURE_ATLAS_DIM, NVRenderTextureFormats::Alpha8); + } + m_textureAtlas->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + m_textureAtlas->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + m_textureAtlas->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge); + m_textureAtlas->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge); + qt3ds::render::STextureDetails texTexDetails = m_textureAtlas->GetTextureDetails(); + return TTextTextureAtlasDetailsAndTexture( + STextTextureAtlasDetails(texTexDetails.m_Height, texTexDetails.m_Height, false, + count), + m_textureAtlas); + } + } + + return TTextTextureAtlasDetailsAndTexture(STextTextureAtlasDetails(), NULL); + } + +private: + bool m_TextureAtlasInitialized; + NVRenderTexture2D *m_textureAtlas; // this is the actual texture which has application lifetime +}; + +} // namespace + +ITextTextureAtlas &ITextTextureAtlas::CreateTextureAtlas(NVFoundationBase &inFnd, + ITextRenderer &inTextRenderer, + NVRenderContext &inRenderContext) +{ + return *QT3DS_NEW(inFnd.getAllocator(), STextTextureAtlas)(inFnd, inTextRenderer, inRenderContext); +} diff --git a/src/runtimerender/Qt3DSRenderTextTextureAtlas.h b/src/runtimerender/Qt3DSRenderTextTextureAtlas.h new file mode 100644 index 0000000..7a3f121 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextTextureAtlas.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TEXT_TEXTURE_ATLAS_H +#define QT3DS_RENDER_TEXT_TEXTURE_ATLAS_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "Qt3DSRenderText.h" +#include "EASTL/algorithm.h" + +namespace qt3ds { +namespace render { + + class ITextRenderer; + + typedef eastl::pair<STextTextureAtlasDetails, NVScopedRefCounted<NVRenderTexture2D>> + TTextTextureAtlasDetailsAndTexture; + typedef eastl::pair<SRenderTextureAtlasDetails, NVScopedRefCounted<NVRenderTexture2D>> + TTextRenderAtlasDetailsAndTexture; + + class ITextTextureAtlas : public NVRefCounted + { + protected: + virtual ~ITextTextureAtlas() {} + public: + virtual TTextRenderAtlasDetailsAndTexture RenderText(const STextRenderInfo &inText) = 0; + virtual bool IsInitialized() = 0; + virtual TTextTextureAtlasDetailsAndTexture PrepareTextureAtlas() = 0; + + static ITextTextureAtlas &CreateTextureAtlas(NVFoundationBase &inFnd, + ITextRenderer &inTextRenderer, + NVRenderContext &inRenderContext); + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderTextTextureCache.cpp b/src/runtimerender/Qt3DSRenderTextTextureCache.cpp new file mode 100644 index 0000000..8edbeca --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextTextureCache.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSTextRenderer.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" +#include "foundation/Qt3DSPool.h" + +using namespace qt3ds::render; + +namespace eastl { +template <> +struct hash<STextRenderInfo> +{ + size_t operator()(const qt3ds::render::STextRenderInfo &inInfo) const + { + size_t retval = hash<size_t>()(reinterpret_cast<size_t>(inInfo.m_Text.c_str())); + retval = retval ^ hash<size_t>()(reinterpret_cast<size_t>(inInfo.m_Font.c_str())); + retval = retval ^ hash<float>()(inInfo.m_FontSize); + retval = retval ^ hash<int>()(static_cast<int>(inInfo.m_HorizontalAlignment)); + retval = retval ^ hash<int>()(static_cast<int>(inInfo.m_VerticalAlignment)); + retval = retval ^ hash<float>()(inInfo.m_Leading); + retval = retval ^ hash<float>()(inInfo.m_Tracking); + retval = retval ^ hash<bool>()(inInfo.m_DropShadow); + retval = retval ^ hash<float>()(inInfo.m_DropShadowStrength); + retval = retval ^ hash<float>()(inInfo.m_DropShadowOffsetX); + retval = retval ^ hash<float>()(inInfo.m_DropShadowOffsetY); + retval = retval ^ hash<float>()(inInfo.m_BoundingBox.x); + retval = retval ^ hash<float>()(inInfo.m_BoundingBox.y); + retval = retval ^ hash<int>()(static_cast<int>(inInfo.m_Elide)); + retval = retval ^ hash<int>()(static_cast<int>(inInfo.m_WordWrap)); + retval = retval ^ hash<bool>()(inInfo.m_EnableAcceleratedFont); + return retval; + } +}; +} + +namespace { +struct STextRenderInfoAndHash +{ + STextRenderInfo m_Info; + QT3DSF32 m_ScaleFactor; + size_t m_Hashcode; + STextRenderInfoAndHash(const STextRenderInfo &inInfo, QT3DSF32 inScaleFactor) + : m_Info(inInfo) + , m_ScaleFactor(inScaleFactor) + , m_Hashcode(eastl::hash<STextRenderInfo>()(inInfo) ^ eastl::hash<float>()(inScaleFactor)) + { + } + bool operator==(const STextRenderInfoAndHash &inOther) const + { + return m_Info.m_Text == inOther.m_Info.m_Text && m_Info.m_Font == inOther.m_Info.m_Font + && m_Info.m_FontSize == inOther.m_Info.m_FontSize + && m_Info.m_HorizontalAlignment == inOther.m_Info.m_HorizontalAlignment + && m_Info.m_VerticalAlignment == inOther.m_Info.m_VerticalAlignment + && m_Info.m_Leading == inOther.m_Info.m_Leading + && m_Info.m_Tracking == inOther.m_Info.m_Tracking + && m_Info.m_DropShadow == inOther.m_Info.m_DropShadow + && m_Info.m_DropShadowStrength == inOther.m_Info.m_DropShadowStrength + && m_Info.m_DropShadowOffsetX == inOther.m_Info.m_DropShadowOffsetX + && m_Info.m_DropShadowOffsetY == inOther.m_Info.m_DropShadowOffsetY + && m_Info.m_BoundingBox == inOther.m_Info.m_BoundingBox + && m_Info.m_WordWrap == inOther.m_Info.m_WordWrap + && m_Info.m_EnableAcceleratedFont == inOther.m_Info.m_EnableAcceleratedFont + && m_ScaleFactor == inOther.m_ScaleFactor; + } +}; +} + +namespace eastl { +template <> +struct hash<STextRenderInfoAndHash> +{ + size_t operator()(const STextRenderInfoAndHash &inInfo) const { return inInfo.m_Hashcode; } +}; +}; + +namespace { + +struct STextCacheNode +{ + STextCacheNode *m_PreviousSibling; + STextCacheNode *m_NextSibling; + STextRenderInfoAndHash m_RenderInfo; + TTPathObjectAndTexture m_TextInfo; + QT3DSU32 m_FrameCount; + + STextCacheNode(const STextRenderInfoAndHash &inRenderInfo, + const TTPathObjectAndTexture &inTextInfo) + : m_PreviousSibling(NULL) + , m_NextSibling(NULL) + , m_RenderInfo(inRenderInfo) + , m_TextInfo(inTextInfo) + , m_FrameCount(0) + { + } +}; + +typedef nvhash_map<STextRenderInfoAndHash, STextCacheNode *> TTextureInfoHash; + +DEFINE_INVASIVE_LIST(TextCacheNode); +IMPLEMENT_INVASIVE_LIST(TextCacheNode, m_PreviousSibling, m_NextSibling); + +struct STextTextureCache : public ITextTextureCache +{ + typedef Pool<STextCacheNode, ForwardingAllocator> TPoolType; + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + NVScopedRefCounted<ITextRenderer> m_TextRenderer; + TTextureInfoHash m_TextureCache; + TTextCacheNodeList m_LRUList; + TPoolType m_CacheNodePool; + QT3DSU32 m_HighWaterMark; + QT3DSU32 m_FrameCount; + QT3DSU32 m_TextureTotalBytes; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + bool m_CanUsePathRendering; ///< true if we use hardware accelerated font rendering + + STextTextureCache(NVFoundationBase &inFnd, ITextRenderer &inRenderer, + NVRenderContext &inRenderContext) + : m_Foundation(inFnd) + , mRefCount(0) + , m_TextRenderer(inRenderer) + , m_TextureCache(m_Foundation.getAllocator(), "STextTextureCache::m_TextureCache") + , m_CacheNodePool(ForwardingAllocator(m_Foundation.getAllocator(), + "STextTextureCache::m_CacheNodePool")) + , m_HighWaterMark(0x100000) + , m_FrameCount(0) + , m_TextureTotalBytes(0) + , m_RenderContext(inRenderContext) + { + // hardware accelerate font rendering not ready yet + m_CanUsePathRendering = (m_RenderContext->IsPathRenderingSupported() + && m_RenderContext->IsProgramPipelineSupported()); + } + + virtual ~STextTextureCache() + { + for (TTextCacheNodeList::iterator iter = m_LRUList.begin(), end = m_LRUList.end(); + iter != end; ++iter) + iter->~STextCacheNode(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + static inline QT3DSU32 GetNumBytes(NVRenderTexture2D &inTexture) + { + STextureDetails theDetails(inTexture.GetTextureDetails()); + return theDetails.m_Width * theDetails.m_Height + * NVRenderTextureFormats::getSizeofFormat(theDetails.m_Format); + } + + NVScopedRefCounted<NVRenderTexture2D> InvalidateLastItem() + { + NVScopedRefCounted<NVRenderTexture2D> nextTexture; + if (m_LRUList.empty() == false) { + STextCacheNode &theEnd = m_LRUList.back(); + if (theEnd.m_FrameCount != m_FrameCount) { + nextTexture = theEnd.m_TextInfo.second.second; + STextureDetails theDetails = nextTexture->GetTextureDetails(); + m_TextureTotalBytes -= GetNumBytes(*nextTexture.mPtr); + m_LRUList.remove(theEnd); + // copy the key because the next statement will destroy memory + m_TextureCache.erase(theEnd.m_RenderInfo); + theEnd.~STextCacheNode(); + m_CacheNodePool.deallocate(&theEnd); + } + } + return nextTexture; + } + + TTPathObjectAndTexture RenderText(const STextRenderInfo &inText, QT3DSF32 inScaleFactor) override + { + STextRenderInfoAndHash theKey(inText, inScaleFactor); + TTextureInfoHash::iterator theFind( + m_TextureCache.find(theKey)); + STextCacheNode *retval = NULL; + if (theFind != m_TextureCache.end()) { + retval = theFind->second; + m_LRUList.remove(*retval); + } else { + NVScopedRefCounted<NVRenderTexture2D> nextTexture; + if (m_TextureTotalBytes >= m_HighWaterMark && m_LRUList.empty() == false) + nextTexture = InvalidateLastItem(); + + if (nextTexture.mPtr == NULL) + nextTexture = m_RenderContext->CreateTexture2D(); + + NVScopedRefCounted<NVRenderPathFontItem> nextPathFontItemObject; + NVScopedRefCounted<NVRenderPathFontSpecification> nextPathFontObject; + // HW acceleration for fonts not supported + //if (m_CanUsePathRendering && inText.m_EnableAcceleratedFont) { + // nextPathFontItemObject = m_RenderContext->CreatePathFontItem(); + // nextPathFontObject = m_RenderContext->CreatePathFontSpecification(inText.m_Font); + //} + + STextRenderInfo theTextInfo(inText); + theTextInfo.m_FontSize *= inScaleFactor; + STextTextureDetails theDetails; + + + // HW acceleration for fonts not supported + //if (!m_CanUsePathRendering || !inText.m_EnableAcceleratedFont) + theDetails = m_TextRenderer->RenderText(theTextInfo, *nextTexture.mPtr); + //else + // theDetails = m_TextRenderer->RenderText(theTextInfo, *nextPathFontItemObject.mPtr, + // *nextPathFontObject.mPtr); + + if (fabs(inScaleFactor - 1.0f) > .001f) { + TTPathObjectAndTexture theCanonicalDetails = RenderText(inText, 1.0f); + theDetails.m_ScaleFactor.x = + (QT3DSF32)theDetails.m_TextWidth / theCanonicalDetails.second.first.m_TextWidth; + theDetails.m_ScaleFactor.y = + (QT3DSF32)theDetails.m_TextHeight / theCanonicalDetails.second.first.m_TextHeight; + } + retval = m_CacheNodePool.construct( + theKey, TTPathObjectAndTexture( + TPathFontSpecAndPathObject(nextPathFontObject, nextPathFontItemObject), + TTextTextureDetailsAndTexture(theDetails, nextTexture)), + __FILE__, __LINE__); + TTextureInfoHash::iterator insert = + m_TextureCache.insert(eastl::make_pair(theKey, retval)).first; + if (!m_CanUsePathRendering) + m_TextureTotalBytes += GetNumBytes(*(retval->m_TextInfo.second.second.mPtr)); + } + retval->m_FrameCount = m_FrameCount; + m_LRUList.push_front(*retval); + return retval->m_TextInfo; + } + // We may have one more texture in cache than this byte count, but this will be the limiting + // factor. + QT3DSU32 GetCacheHighWaterBytes() const override { return m_HighWaterMark; } + // default cache size is 10 MB. + void SetCacheHighWaterBytes(QT3DSU32 inByteCount) override { m_HighWaterMark = inByteCount; } + + void BeginFrame() override {} + void EndFrame() override + { + // algorithm is resistant to rollover. + ++m_FrameCount; + // Release any texture that put us over the limit. + // This almost guarantees thrashing if the limit is set too low. Enable at your + // own risk at *TEST CAREFULLY* + /* + while( m_TextureTotalBytes >= m_HighWaterMark && m_LRUList.empty() == false ) + InvalidateLastItem(); + */ + } +}; +} + +ITextTextureCache &ITextTextureCache::CreateTextureCache(NVFoundationBase &inFnd, + ITextRenderer &inTextRenderer, + NVRenderContext &inRenderContext) +{ + return *QT3DS_NEW(inFnd.getAllocator(), STextTextureCache)(inFnd, inTextRenderer, inRenderContext); +} diff --git a/src/runtimerender/Qt3DSRenderTextTextureCache.h b/src/runtimerender/Qt3DSRenderTextTextureCache.h new file mode 100644 index 0000000..45d4021 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextTextureCache.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TEXT_TEXTURE_CACHE_H +#define QT3DS_RENDER_TEXT_TEXTURE_CACHE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "Qt3DSRenderText.h" +#include "EASTL/algorithm.h" + +namespace qt3ds { +namespace render { + + class ITextRenderer; + + typedef eastl::pair<NVScopedRefCounted<NVRenderPathFontSpecification>, + NVScopedRefCounted<NVRenderPathFontItem>> + TPathFontSpecAndPathObject; + typedef eastl::pair<STextTextureDetails, NVScopedRefCounted<NVRenderTexture2D>> + TTextTextureDetailsAndTexture; + typedef eastl::pair<TPathFontSpecAndPathObject, TTextTextureDetailsAndTexture> + TTPathObjectAndTexture; + + class ITextTextureCache : public NVRefCounted + { + protected: + virtual ~ITextTextureCache() {} + public: + virtual TTPathObjectAndTexture RenderText(const STextRenderInfo &inText, + QT3DSF32 inScaleFactor) = 0; + // We may have one more texture in cache than this byte count, but this will be the limiting + // factor. + virtual QT3DSU32 GetCacheHighWaterBytes() const = 0; + virtual void SetCacheHighWaterBytes(QT3DSU32 inNumBytes) = 0; + + virtual void BeginFrame() = 0; + // We need to know the frame rhythm because we can't release anything that was touched this + // frame. + virtual void EndFrame() = 0; + + static ITextTextureCache &CreateTextureCache(NVFoundationBase &inFnd, + ITextRenderer &inTextRenderer, + NVRenderContext &inRenderContext); + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderTextTypes.h b/src/runtimerender/Qt3DSRenderTextTypes.h new file mode 100644 index 0000000..bbaaa38 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextTypes.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2015 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TEXT_TYPES_H +#define QT3DS_RENDER_TEXT_TYPES_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSVec2.h" + +namespace qt3ds { +namespace render { + + struct TextHorizontalAlignment + { + enum Enum { + Unknown = 0, + Left, + Center, + Right, + }; + }; + + struct TextVerticalAlignment + { + enum Enum { + Unknown = 0, + Top, + Middle, + Bottom, + }; + }; + + struct TextWordWrap + { + enum Enum { + Unknown = 0, + Clip, + WrapWord, + WrapAnywhere, + }; + }; + + struct TextElide + { + enum Enum { + ElideNone = 0, + ElideLeft, + ElideMiddle, + ElideRight, + }; + }; + + struct STextDimensions + { + QT3DSU32 m_TextWidth; + QT3DSU32 m_TextHeight; + STextDimensions(QT3DSU32 w, QT3DSU32 h) + : m_TextWidth(w) + , m_TextHeight(h) + { + } + STextDimensions() + : m_TextWidth(0) + , m_TextHeight(0) + { + } + }; + + struct STextTextureDetails : public STextDimensions + { + QT3DSVec2 m_ScaleFactor; + bool m_FlipY; + STextTextureDetails(QT3DSU32 w, QT3DSU32 h, bool inFlipY, QT3DSVec2 scaleF) + : STextDimensions(w, h) + , m_ScaleFactor(scaleF) + , m_FlipY(inFlipY) + { + } + STextTextureDetails() + : m_ScaleFactor(1.0f) + , m_FlipY(false) + { + } + }; + + struct STextTextureAtlasEntryDetails : public STextDimensions + { + QT3DSI32 m_X, m_Y; + STextTextureAtlasEntryDetails(QT3DSU32 w, QT3DSU32 h, QT3DSI32 x, QT3DSI32 y) + : STextDimensions(w, h) + , m_X(x) + , m_Y(y) + { + } + STextTextureAtlasEntryDetails() + : m_X(0) + , m_Y(0) + { + } + }; + + struct SRenderTextureAtlasDetails + { + QT3DSU32 m_VertexCount; + NVDataRef<QT3DSU8> m_Vertices; + + SRenderTextureAtlasDetails(QT3DSU32 count, NVDataRef<QT3DSU8> inVertices) + : m_VertexCount(count) + , m_Vertices(inVertices) + { + } + SRenderTextureAtlasDetails() + : m_VertexCount(0) + , m_Vertices(NVDataRef<QT3DSU8>()) + { + } + }; + + struct STextTextureAtlasDetails : public STextTextureDetails + { + QT3DSU32 m_EntryCount; + STextTextureAtlasDetails(QT3DSU32 w, QT3DSU32 h, bool inFlipY, QT3DSU32 count) + : STextTextureDetails(w, h, inFlipY, QT3DSVec2(1.0f)) + , m_EntryCount(count) + { + } + STextTextureAtlasDetails() + : m_EntryCount(0) + { + } + }; + + // Adding/removing a member to this object means you need to update the texture cache code + // - UICRenderTextTextureCache.cpp + + struct STextRenderInfo + { + CRegisteredString m_Text; + CRegisteredString m_Font; + QT3DSF32 m_FontSize; + TextHorizontalAlignment::Enum m_HorizontalAlignment; + TextVerticalAlignment::Enum m_VerticalAlignment; + QT3DSF32 m_Leading; // space between lines + QT3DSF32 m_Tracking; // space between letters + bool m_DropShadow; + QT3DSF32 m_DropShadowStrength; + QT3DSF32 m_DropShadowOffsetX; + QT3DSF32 m_DropShadowOffsetY; + TextWordWrap::Enum m_WordWrap; + QT3DSVec2 m_BoundingBox; + TextElide::Enum m_Elide; + + QT3DSF32 m_ScaleX; // Pixel scale in X + QT3DSF32 m_ScaleY; // Pixel scale in Y + + bool m_EnableAcceleratedFont; // use NV path rendering + + STextRenderInfo(); + ~STextRenderInfo(); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderTextureAtlas.cpp b/src/runtimerender/Qt3DSRenderTextureAtlas.cpp new file mode 100644 index 0000000..062e7d5 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextureAtlas.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderTextureAtlas.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "render/Qt3DSRenderContext.h" + +using namespace qt3ds::render; + +namespace { + +// a algorithm based on http://clb.demon.fi/files/RectangleBinPack/ +struct STextureAtlasBinPackSL +{ +public: + STextureAtlasBinPackSL(NVRenderContext &inContext, QT3DSI32 width, QT3DSI32 height) + : m_BinWidth(width) + , m_BinHeight(height) + , m_SkyLine(inContext.GetAllocator(), "STextureAtlasBinPackSL::m_SkyLine") + { + // setup first entry + SSkylineNode theNode = { 0, 0, width }; + m_SkyLine.push_back(theNode); + } + + ~STextureAtlasBinPackSL() { m_SkyLine.clear(); } + + /* insert new rect + * + */ + STextureAtlasRect Insert(QT3DSI32 width, QT3DSI32 height) + { + QT3DSI32 binHeight; + QT3DSI32 binWidth; + QT3DSI32 binIndex; + + STextureAtlasRect newNode = findPosition(width, height, &binWidth, &binHeight, &binIndex); + + if (binIndex != -1) { + // adjust skyline nodes + addSkylineLevelNode(binIndex, newNode); + } + + return newNode; + } + +private: + /// Represents a single level (a horizontal line) of the skyline/horizon/envelope. + struct SSkylineNode + { + int x; ///< The starting x-coordinate (leftmost). + int y; ///< The y-coordinate of the skyline level line. + int width; /// The line width. The ending coordinate (inclusive) will be x+width-1. + }; + + /* find position + * + */ + STextureAtlasRect findPosition(QT3DSI32 width, QT3DSI32 height, QT3DSI32 *binWidth, QT3DSI32 *binHeight, + QT3DSI32 *binIndex) + { + *binWidth = m_BinWidth; + *binHeight = m_BinHeight; + *binIndex = -1; + STextureAtlasRect newRect; + + for (QT3DSU32 i = 0; i < m_SkyLine.size(); ++i) { + QT3DSI32 y = getSkylineLevel(i, width, height); + + if (y >= 0) { + if ((y + height < *binHeight) + || ((y + height == *binHeight) && m_SkyLine[i].width < *binWidth)) { + *binHeight = y + height; + *binIndex = i; + *binWidth = m_SkyLine[i].width; + newRect.m_X = m_SkyLine[i].x; + newRect.m_Y = y; + newRect.m_Width = width; + newRect.m_Height = height; + } + } + } + + return newRect; + } + + /* @brief check if rectangle can be placed into the the bin + * + * return skyline hight + */ + int getSkylineLevel(QT3DSU32 binIndex, QT3DSI32 width, QT3DSI32 height) + { + // first check width exceed + QT3DSI32 x = m_SkyLine[binIndex].x; + if (x + width > m_BinWidth) + return -1; + + QT3DSI32 leftAlign = width; + QT3DSU32 index = binIndex; + QT3DSI32 y = m_SkyLine[index].y; + + while (leftAlign > 0) { + y = (y > m_SkyLine[index].y) ? y : m_SkyLine[index].y; + // check hight + if (y + height > m_BinHeight) + return -1; + + leftAlign -= m_SkyLine[index].width; + ++index; + + if (index > m_SkyLine.size()) + return -1; + } + + return y; + } + + /* @brief add an new skyline entry + * + * return no return + */ + void addSkylineLevelNode(QT3DSI32 binIndex, const STextureAtlasRect &newRect) + { + SSkylineNode newNode; + + newNode.x = newRect.m_X; + newNode.y = newRect.m_Y + newRect.m_Height; + newNode.width = newRect.m_Width; + m_SkyLine.insert(m_SkyLine.begin() + binIndex, newNode); + + // iterate over follow up nodes and adjust + for (QT3DSU32 i = binIndex + 1; i < m_SkyLine.size(); ++i) { + if (m_SkyLine[i].x < m_SkyLine[i - 1].x + m_SkyLine[i - 1].width) { + int shrink = m_SkyLine[i - 1].x + m_SkyLine[i - 1].width - m_SkyLine[i].x; + + m_SkyLine[i].x += shrink; + m_SkyLine[i].width -= shrink; + + if (m_SkyLine[i].width <= 0) { + m_SkyLine.erase(m_SkyLine.begin() + i); + --i; + } else { + break; + } + } else { + break; + } + } + + mergeSkylineLevelNodes(); + } + + /* @brief merge skyline node + * + * return no return + */ + void mergeSkylineLevelNodes() + { + // check if we can merge nodes + for (QT3DSU32 i = 0; i < m_SkyLine.size() - 1; ++i) { + if (m_SkyLine[i].y == m_SkyLine[i + 1].y) { + m_SkyLine[i].width += m_SkyLine[i + 1].width; + m_SkyLine.erase(m_SkyLine.begin() + (i + 1)); + --i; + } + } + } + + QT3DSI32 m_BinWidth; + QT3DSI32 m_BinHeight; + + nvvector<SSkylineNode> m_SkyLine; +}; + +struct STextureAtlasEntry +{ + STextureAtlasEntry() + : m_X(0) + , m_Y(0) + , m_Width(0) + , m_Height(0) + , m_pBuffer(NVDataRef<QT3DSU8>()) + { + } + STextureAtlasEntry(QT3DSF32 x, QT3DSF32 y, QT3DSF32 w, QT3DSF32 h, NVDataRef<QT3DSU8> buffer) + : m_X(x) + , m_Y(y) + , m_Width(w) + , m_Height(h) + , m_pBuffer(buffer) + { + } + STextureAtlasEntry(const STextureAtlasEntry &entry) + { + m_X = entry.m_X; + m_Y = entry.m_Y; + m_Width = entry.m_Width; + m_Height = entry.m_Height; + m_pBuffer = entry.m_pBuffer; + } + ~STextureAtlasEntry() {} + + QT3DSF32 m_X, m_Y; + QT3DSF32 m_Width, m_Height; + NVDataRef<QT3DSU8> m_pBuffer; +}; + +struct STextureAtlas : public ITextureAtlas +{ + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + + STextureAtlas(NVFoundationBase &inFnd, NVRenderContext &inRenderContext, QT3DSI32 width, + QT3DSI32 height) + : m_Foundation(inFnd) + , mRefCount(0) + , m_RenderContext(inRenderContext) + , m_Width(width) + , m_Height(height) + , m_Spacing(1) + , m_AtlasEntrys(inFnd.getAllocator(), "STextureAtlas::m_SkyLine") + { + m_pBinPack = + QT3DS_NEW(inFnd.getAllocator(), STextureAtlasBinPackSL)(inRenderContext, width, height); + } + + virtual ~STextureAtlas() + { + RelaseEntries(); + + if (m_pBinPack) + NVDelete(m_Foundation.getAllocator(), m_pBinPack); + } + + void RelaseEntries() override + { + nvvector<STextureAtlasEntry>::iterator it; + + for (it = m_AtlasEntrys.begin(); it != m_AtlasEntrys.end(); it++) { + QT3DS_FREE(m_Foundation.getAllocator(), it->m_pBuffer.begin()); + } + + m_AtlasEntrys.clear(); + } + QT3DSI32 GetWidth() const override { return m_Width; } + QT3DSI32 GetHeight() const override { return m_Height; } + + QT3DSI32 GetAtlasEntryCount() const override { return m_AtlasEntrys.size(); } + + TTextureAtlasEntryAndBuffer GetAtlasEntryByIndex(QT3DSU32 index) override + { + if (index >= m_AtlasEntrys.size()) + return eastl::make_pair(STextureAtlasRect(), NVDataRef<QT3DSU8>()); + + return eastl::make_pair(STextureAtlasRect((QT3DSI32)m_AtlasEntrys[index].m_X, + (QT3DSI32)m_AtlasEntrys[index].m_Y, + (QT3DSI32)m_AtlasEntrys[index].m_Width, + (QT3DSI32)m_AtlasEntrys[index].m_Height), + m_AtlasEntrys[index].m_pBuffer); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + STextureAtlasRect AddAtlasEntry(QT3DSI32 width, QT3DSI32 height, QT3DSI32 pitch, + QT3DSI32 dataWidth, NVConstDataRef<QT3DSU8> bufferData) override + { + STextureAtlasRect rect; + + // pitch is the number of bytes per line in bufferData + // dataWidth is the relevant data width in bufferData. Rest is padding that can be ignored. + if (m_pBinPack) { + QT3DSI32 paddedWith, paddedPitch, paddedHeight; + // add spacing around the character + paddedWith = width + 2 * m_Spacing; + paddedPitch = dataWidth + 2 * m_Spacing; + paddedHeight = height + 2 * m_Spacing; + // first get entry in the texture atlas + rect = m_pBinPack->Insert(paddedWith, paddedHeight); + if (rect.m_Width == 0) + return rect; + + // we align the data be to 4 byte + int alignment = (4 - (paddedPitch % 4)) % 4; + paddedPitch += alignment; + + // since we do spacing around the character we need to copy line by line + QT3DSU8 *glyphBuffer = + (QT3DSU8 *)QT3DS_ALLOC(m_Foundation.getAllocator(), + paddedHeight * paddedPitch * sizeof(QT3DSU8), "STextureAtlas"); + if (glyphBuffer) { + memset(glyphBuffer, 0, paddedHeight * paddedPitch); + + QT3DSU8 *pDst = glyphBuffer + paddedPitch + m_Spacing; + QT3DSU8 *pSrc = const_cast<QT3DSU8 *>(bufferData.begin()); + for (QT3DSI32 i = 0; i < height; ++i) { + memcpy(pDst, pSrc, dataWidth); + + pDst += paddedPitch; + pSrc += pitch; + } + + // add new entry + m_AtlasEntrys.push_back(STextureAtlasEntry( + (QT3DSF32)rect.m_X, (QT3DSF32)rect.m_Y, (QT3DSF32)paddedWith, (QT3DSF32)paddedHeight, + NVDataRef<QT3DSU8>(glyphBuffer, paddedHeight * paddedPitch * sizeof(QT3DSU8)))); + + // normalize texture coordinates + rect.m_NormX = (QT3DSF32)rect.m_X / (QT3DSF32)m_Width; + rect.m_NormY = (QT3DSF32)rect.m_Y / (QT3DSF32)m_Height; + rect.m_NormWidth = (QT3DSF32)paddedWith / (QT3DSF32)m_Width; + rect.m_NormHeight = (QT3DSF32)paddedHeight / (QT3DSF32)m_Height; + } + } + + return rect; + } + +private: + QT3DSI32 m_Width; ///< texture atlas width + QT3DSI32 m_Height; ///< texture atlas height + QT3DSI32 m_Spacing; ///< spacing around the entry + nvvector<STextureAtlasEntry> m_AtlasEntrys; ///< our entries in the atlas + STextureAtlasBinPackSL *m_pBinPack; ///< our bin packer which actually does most of the work +}; + +} // namespace + +ITextureAtlas &ITextureAtlas::CreateTextureAtlas(NVFoundationBase &inFnd, + NVRenderContext &inRenderContext, QT3DSI32 width, + QT3DSI32 height) +{ + return *QT3DS_NEW(inFnd.getAllocator(), STextureAtlas)(inFnd, inRenderContext, width, height); +} diff --git a/src/runtimerender/Qt3DSRenderTextureAtlas.h b/src/runtimerender/Qt3DSRenderTextureAtlas.h new file mode 100644 index 0000000..46f7bdb --- /dev/null +++ b/src/runtimerender/Qt3DSRenderTextureAtlas.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TEXTURE_ATLAS_H +#define QT3DS_RENDER_TEXTURE_ATLAS_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "EASTL/algorithm.h" + +namespace qt3ds { +namespace render { + + class ITextRenderer; + + struct STextureAtlasRect + { + + STextureAtlasRect() + : m_X(0) + , m_Y(0) + , m_Width(0) + , m_Height(0) + { + } + + STextureAtlasRect(QT3DSI32 x, QT3DSI32 y, QT3DSI32 w, QT3DSI32 h) + : m_X(x) + , m_Y(y) + , m_Width(w) + , m_Height(h) + { + } + + QT3DSI32 m_X; + QT3DSI32 m_Y; + QT3DSI32 m_Width; + QT3DSI32 m_Height; + + // normalized coordinates + QT3DSF32 m_NormX; + QT3DSF32 m_NormY; + QT3DSF32 m_NormWidth; + QT3DSF32 m_NormHeight; + }; + + typedef eastl::pair<STextureAtlasRect, NVDataRef<QT3DSU8>> TTextureAtlasEntryAndBuffer; + + /** + * Abstract class of a texture atlas representation + */ + class ITextureAtlas : public NVRefCounted + { + protected: + virtual ~ITextureAtlas() {} + + public: + virtual QT3DSI32 GetWidth() const = 0; + virtual QT3DSI32 GetHeight() const = 0; + virtual QT3DSI32 GetAtlasEntryCount() const = 0; + virtual TTextureAtlasEntryAndBuffer GetAtlasEntryByIndex(QT3DSU32 index) = 0; + + virtual STextureAtlasRect AddAtlasEntry(QT3DSI32 width, QT3DSI32 height, QT3DSI32 pitch, + QT3DSI32 dataWidth, + NVConstDataRef<QT3DSU8> bufferData) = 0; + virtual void RelaseEntries() = 0; + + static ITextureAtlas &CreateTextureAtlas(NVFoundationBase &inFnd, + NVRenderContext &inRenderContext, QT3DSI32 width, + QT3DSI32 height); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderThreadPool.cpp b/src/runtimerender/Qt3DSRenderThreadPool.cpp new file mode 100644 index 0000000..c9c7323 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderThreadPool.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderThreadPool.h" +#include "foundation/Qt3DSThread.h" +#include "EASTL/utility.h" +#include "EASTL/list.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSPool.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" + +using namespace qt3ds::render; + +namespace { + +struct STaskHeadOp +{ + STask *get(STask &inTask) { return inTask.m_PreviousTask; } + void set(STask &inTask, STask *inItem) { inTask.m_PreviousTask = inItem; } +}; + +struct STaskTailOp +{ + STask *get(STask &inTask) { return inTask.m_NextTask; } + void set(STask &inTask, STask *inItem) { inTask.m_NextTask = inItem; } +}; + +typedef InvasiveLinkedList<STask, STaskHeadOp, STaskTailOp> TTaskList; + +struct SThreadPoolThread : public Thread +{ + IThreadPool *m_Mgr; + SThreadPoolThread(NVFoundationBase &foundation, IThreadPool *inMgr) + : Thread(foundation) + , m_Mgr(inMgr) + { + } + void execute(void) override + { + setName("Qt3DSRender Thread manager thread"); + while (!quitIsSignalled()) { + STask task = m_Mgr->GetNextTask(); + if (task.m_Function) { + task.CallFunction(); + m_Mgr->TaskFinished(task.m_Id); + } + } + quit(); + } +}; + +struct SThreadPool : public IThreadPool +{ + typedef nvhash_map<QT3DSU64, STask *> TIdTaskMap; + typedef Mutex::ScopedLock TLockType; + typedef Pool<STask, ForwardingAllocator> TTaskPool; + + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + nvvector<SThreadPoolThread *> m_Threads; + TIdTaskMap m_Tasks; + Sync m_TaskListEvent; + volatile bool m_Running; + Mutex m_TaskListMutex; + TTaskPool m_TaskPool; + TTaskList m_TaskList; + + QT3DSU64 m_NextId; + + SThreadPool(NVFoundationBase &inBase, QT3DSU32 inMaxThreads) + : m_Foundation(inBase) + , mRefCount(0) + , m_Threads(inBase.getAllocator(), "SThreadPool::m_Threads") + , m_Tasks(inBase.getAllocator(), "SThreadPool::m_Tasks") + , m_TaskListEvent(inBase.getAllocator()) + , m_Running(true) + , m_TaskListMutex(m_Foundation.getAllocator()) + , m_TaskPool(ForwardingAllocator(m_Foundation.getAllocator(), "SThreadPool::m_TaskPool")) + , m_NextId(1) + { + // Fire up our little pools of chaos. + for (QT3DSU32 idx = 0; idx < inMaxThreads; ++idx) { + m_Threads.push_back( + QT3DS_NEW(m_Foundation.getAllocator(), SThreadPoolThread)(m_Foundation, this)); + m_Threads.back()->start(Thread::DEFAULT_STACK_SIZE); + } + } + + void MutexHeldRemoveTaskFromList(STask *theTask) + { + if (theTask) + m_TaskList.remove(*theTask); + QT3DS_ASSERT(theTask->m_NextTask == NULL); + QT3DS_ASSERT(theTask->m_PreviousTask == NULL); + } + + STask *MutexHeldNextTask() + { + STask *theTask = m_TaskList.front_ptr(); + if (theTask) { + MutexHeldRemoveTaskFromList(theTask); + } + if (theTask) { + QT3DS_ASSERT(m_TaskList.m_Head != theTask); + QT3DS_ASSERT(m_TaskList.m_Tail != theTask); + } + return theTask; + } + + virtual ~SThreadPool() + { + m_Running = false; + + m_TaskListEvent.set(); + + for (QT3DSU32 idx = 0, end = m_Threads.size(); idx < end; ++idx) + m_Threads[idx]->signalQuit(); + + for (QT3DSU32 idx = 0, end = m_Threads.size(); idx < end; ++idx) { + m_Threads[idx]->waitForQuit(); + NVDelete(m_Foundation.getAllocator(), m_Threads[idx]); + } + + m_Threads.clear(); + + TLockType __listMutexLocker(m_TaskListMutex); + + for (STask *theTask = MutexHeldNextTask(); theTask; theTask = MutexHeldNextTask()) { + theTask->Cancel(); + } + + m_Tasks.clear(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void VerifyTaskList() + { + STask *theLastTask = NULL; + for (STask *theTask = m_TaskList.m_Head; theTask; theTask = theTask->m_NextTask) { + QT3DS_ASSERT(theTask->m_PreviousTask == theLastTask); + theLastTask = theTask; + } + theLastTask = NULL; + for (STask *theTask = m_TaskList.m_Tail; theTask; theTask = theTask->m_PreviousTask) { + QT3DS_ASSERT(theTask->m_NextTask == theLastTask); + theLastTask = theTask; + } + } + + QT3DSU64 AddTask(void *inUserData, TTaskFunction inFunction, + TTaskFunction inCancelFunction) override + { + if (inFunction && m_Running) { + TLockType __listMutexLocker(m_TaskListMutex); + QT3DSU64 taskId = m_NextId; + ++m_NextId; + + STask *theTask = (STask *)m_TaskPool.allocate(__FILE__, __LINE__); + new (theTask) STask(inUserData, inFunction, inCancelFunction, taskId); + TIdTaskMap::iterator theTaskIter = + m_Tasks.insert(eastl::make_pair(taskId, theTask)).first; + + m_TaskList.push_back(*theTask); + QT3DS_ASSERT(m_TaskList.m_Tail == theTask); + +#ifdef _DEBUG + VerifyTaskList(); +#endif + m_TaskListEvent.set(); + m_TaskListEvent.reset(); + return taskId; + } + QT3DS_ASSERT(false); + return 0; + } + + TaskStates::Enum GetTaskState(QT3DSU64 inTaskId) override + { + TLockType __listMutexLocker(m_TaskListMutex); + TIdTaskMap::iterator theTaskIter = m_Tasks.find(inTaskId); + if (theTaskIter != m_Tasks.end()) + return theTaskIter->second->m_TaskState; + return TaskStates::UnknownTask; + } + + CancelReturnValues::Enum CancelTask(QT3DSU64 inTaskId) override + { + TLockType __listMutexLocker(m_TaskListMutex); + TIdTaskMap::iterator theTaskIter = m_Tasks.find(inTaskId); + if (theTaskIter == m_Tasks.end()) + return CancelReturnValues::TaskCanceled; + if (theTaskIter->second->m_TaskState == TaskStates::Running) + return CancelReturnValues::TaskRunning; + + STask *theTask = theTaskIter->second; + theTask->Cancel(); + MutexHeldRemoveTaskFromList(theTask); + m_Tasks.erase(inTaskId); + m_TaskPool.deallocate(theTask); + + return CancelReturnValues::TaskCanceled; + } + + STask GetNextTask() override + { + + if (m_Running) { + { + TLockType __listMutexLocker(m_TaskListMutex); + STask *retval = MutexHeldNextTask(); + if (retval) + return *retval; + } + // If we couldn't get a task then wait. + m_TaskListEvent.wait(1000); + } + return STask(); + } + + void TaskFinished(QT3DSU64 inId) override + { + TLockType __listMutexLocker(m_TaskListMutex); + TIdTaskMap::iterator theTaskIter = m_Tasks.find(inId); + if (theTaskIter == m_Tasks.end()) { + QT3DS_ASSERT(false); + return; + } + + STask *theTask(theTaskIter->second); + +#ifdef _DEBUG + QT3DS_ASSERT(theTask->m_NextTask == NULL); + QT3DS_ASSERT(theTask->m_PreviousTask == NULL); +#endif + m_TaskPool.deallocate(theTask); + m_Tasks.erase(inId); + return; + } +}; +} + +IThreadPool &IThreadPool::CreateThreadPool(NVFoundationBase &inFoundation, QT3DSU32 inNumThreads) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), SThreadPool)(inFoundation, inNumThreads); +} diff --git a/src/runtimerender/Qt3DSRenderThreadPool.h b/src/runtimerender/Qt3DSRenderThreadPool.h new file mode 100644 index 0000000..990106a --- /dev/null +++ b/src/runtimerender/Qt3DSRenderThreadPool.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_THREAD_POOL_H +#define QT3DS_RENDER_THREAD_POOL_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" + +namespace qt3ds { +namespace render { + + typedef void (*TTaskFunction)(void *inUserData); + +struct TaskStates +{ + enum Enum { + UnknownTask = 0, + Queued, + Running, + }; +}; + + +struct STask +{ + void *m_UserData; + TTaskFunction m_Function; + TTaskFunction m_CancelFunction; + QT3DSU64 m_Id; + TaskStates::Enum m_TaskState; + STask *m_NextTask; + STask *m_PreviousTask; + + STask(void *ud, TTaskFunction func, TTaskFunction cancelFunc, QT3DSU64 inId) + : m_UserData(ud) + , m_Function(func) + , m_CancelFunction(cancelFunc) + , m_Id(inId) + , m_TaskState(TaskStates::Queued) + , m_NextTask(NULL) + , m_PreviousTask(NULL) + { + } + STask() + : m_UserData(NULL) + , m_Function(NULL) + , m_CancelFunction(NULL) + , m_Id(0) + , m_TaskState(TaskStates::UnknownTask) + , m_NextTask(NULL) + , m_PreviousTask(NULL) + { + } + void CallFunction() + { + if (m_Function) + m_Function(m_UserData); + } + void Cancel() + { + if (m_CancelFunction) + m_CancelFunction(m_UserData); + } +}; + + + struct CancelReturnValues + { + enum Enum { + TaskCanceled = 0, + TaskRunning, + TaskNotFound, + }; + }; + + class IThreadPool : public NVRefCounted + { + protected: + virtual ~IThreadPool() {} + public: + // Add a task to be run at some point in the future. + // Tasks will be run roughly in order they are given. + // The returned value is a handle that can be used to query + // details about the task + // Cancel function will be called if the thread pool is destroyed or + // of the task gets canceled. + virtual QT3DSU64 AddTask(void *inUserData, TTaskFunction inFunction, + TTaskFunction inCancelFunction) = 0; + virtual TaskStates::Enum GetTaskState(QT3DSU64 inTaskId) = 0; + virtual CancelReturnValues::Enum CancelTask(QT3DSU64 inTaskId) = 0; + + virtual STask GetNextTask() = 0; + virtual void TaskFinished(QT3DSU64 inId) = 0; + + static IThreadPool &CreateThreadPool(NVFoundationBase &inFoundation, + QT3DSU32 inNumThreads = 4); + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderUIPLoader.cpp b/src/runtimerender/Qt3DSRenderUIPLoader.cpp new file mode 100644 index 0000000..9aad397 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderUIPLoader.cpp @@ -0,0 +1,2089 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#ifdef QT3DS_RENDER_ENABLE_LOAD_UIP + +#include "Qt3DSRenderUIPLoader.h" +#include "Qt3DSRenderPresentation.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderModel.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderUIPSharedTranslation.h" +#include <vector> +#include <map> +#include <set> +#ifdef EA_PLATFORM_WINDOWS +#pragma warning(disable : 4201) +#endif +#include "Qt3DSDMXML.h" +#include "Qt3DSTypes.h" +#include "Qt3DSVector3.h" +#include "Qt3DSMetadata.h" +#include "Qt3DSDMWStrOps.h" +#include "Qt3DSDMWStrOpsImpl.h" +#include "foundation/Qt3DSOption.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSDMComposerTypeDefinitions.h" +#include "EASTL/string.h" +#include "foundation/StrConvertUTF.h" +#include "Qt3DSRenderEffectSystem.h" +#include "StringTools.h" +#include "foundation/FileTools.h" +#include "Qt3DSRenderDynamicObjectSystemCommands.h" +#include "EASTL/map.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSDMMetaDataTypes.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderPluginPropertyValue.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathSubPath.h" +#include "Qt3DSRenderPathManager.h" +#include "q3dsvariantconfig_p.h" + +using qt3ds::foundation::Option; +using qt3ds::foundation::Empty; +using qt3ds::QT3DSF32; +using qt3ds::QT3DSVec3; +using qt3ds::foundation::nvvector; +using qt3ds::QT3DSU32; +using qt3ds::render::RenderLightTypes; +using qt3ds::render::DefaultMaterialLighting; +using qt3ds::render::ImageMappingModes; +using qt3ds::render::DefaultMaterialBlendMode; +using qt3ds::render::NVRenderTextureCoordOp; +using qt3ds::foundation::IStringTable; +using qt3ds::NVFoundationBase; +using namespace qt3ds; +using namespace qt3ds::foundation; +using qt3ds::render::TIdObjectMap; +using qt3ds::render::IBufferManager; +using qt3ds::render::IEffectSystem; +using qt3ds::render::SPresentation; +using qt3ds::render::SScene; +using qt3ds::render::SLayer; +using qt3ds::render::SNode; +using qt3ds::render::SLight; +using qt3ds::render::SCamera; +using qt3ds::render::SModel; +using qt3ds::render::SText; +using qt3ds::render::SDefaultMaterial; +using qt3ds::render::SImage; +using qt3ds::render::SGraphObject; +using qt3ds::render::SDynamicObject; +using qt3ds::render::SEffect; +using qt3ds::render::SCustomMaterial; +using qt3ds::render::GraphObjectTypes; +using qt3ds::render::NodeFlags; +using qt3ds::foundation::CRegisteredString; +using qt3ds::foundation::Qt3DSString; +using qt3ds::foundation::CFileTools; +using qt3ds::render::SReferencedMaterial; +using qt3ds::render::IUIPReferenceResolver; +using qt3ds::render::SPath; +using qt3ds::render::SPathSubPath; +using qt3ds::render::SLightmaps; + +namespace qt3dsdm { +template <> +struct WStrOps<SFloat2> +{ + void StrTo(const char8_t *buffer, SFloat2 &item, nvvector<char8_t> &ioTempBuf) + { + QT3DSU32 len = (QT3DSU32)strlen(buffer); + ioTempBuf.resize(len + 1); + memCopy(ioTempBuf.data(), buffer, (len + 1) * sizeof(char8_t)); + MemoryBuffer<RawAllocator> unused; + qt3dsdm::IStringTable *theTable(NULL); + WCharTReader reader(ioTempBuf.begin(), unused, *theTable); + reader.ReadRef(NVDataRef<QT3DSF32>(item.m_Floats, 2)); + } +}; + +template <> +struct WStrOps<SFloat3> +{ + void StrTo(const char8_t *buffer, SFloat3 &item, nvvector<char8_t> &ioTempBuf) + { + QT3DSU32 len = (QT3DSU32)strlen(buffer); + ioTempBuf.resize(len + 1); + memCopy(ioTempBuf.data(), buffer, (len + 1) * sizeof(char8_t)); + MemoryBuffer<RawAllocator> unused; + qt3dsdm::IStringTable *theTable(NULL); + WCharTReader reader(ioTempBuf.begin(), unused, *theTable); + reader.ReadRef(NVDataRef<QT3DSF32>(item.m_Floats, 3)); + } +}; + +template <> +struct WStrOps<SFloat4> +{ + void StrTo(const char8_t *buffer, SFloat4 &item, nvvector<char8_t> &ioTempBuf) + { + QT3DSU32 len = (QT3DSU32)strlen(buffer); + ioTempBuf.resize(len + 1); + memCopy(ioTempBuf.data(), buffer, (len + 1) * sizeof(char8_t)); + MemoryBuffer<RawAllocator> unused; + qt3dsdm::IStringTable *theTable(NULL); + WCharTReader reader(ioTempBuf.begin(), unused, *theTable); + reader.ReadRef(NVDataRef<QT3DSF32>(item.m_Floats, 4)); + } +}; +} + +namespace { + +typedef eastl::basic_string<char8_t> TStrType; +struct IPropertyParser +{ + virtual ~IPropertyParser() {} + virtual Option<TStrType> ParseStr(const char8_t *inName) = 0; + virtual Option<QT3DSF32> ParseFloat(const char8_t *inName) = 0; + virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName) = 0; + virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName) = 0; + virtual Option<QT3DSVec4> ParseVec4(const char8_t *inName) = 0; + virtual Option<bool> ParseBool(const char8_t *inName) = 0; + virtual Option<QT3DSU32> ParseU32(const char8_t *inName) = 0; + virtual Option<QT3DSI32> ParseI32(const char8_t *inName) = 0; + virtual Option<SGraphObject *> ParseGraphObject(const char8_t *inName) = 0; + virtual Option<SNode *> ParseNode(const char8_t *inName) = 0; +}; +struct SMetaPropertyParser : public IPropertyParser +{ + Q3DStudio::IRuntimeMetaData &m_MetaData; + TStrType m_TempStr; + qt3ds::foundation::CRegisteredString m_Type; + qt3ds::foundation::CRegisteredString m_ClassId; + + SMetaPropertyParser(const char8_t *inType, const char8_t *inClass, + Q3DStudio::IRuntimeMetaData &inMeta) + : m_MetaData(inMeta) + , m_Type(inMeta.GetStringTable()->GetRenderStringTable().RegisterStr(inType)) + , m_ClassId(inMeta.GetStringTable()->GetRenderStringTable().RegisterStr(inClass)) + { + } + + qt3ds::foundation::CRegisteredString Register(const char8_t *inName) + { + return m_MetaData.GetStringTable()->GetRenderStringTable().RegisterStr(inName); + } + + Option<TStrType> ParseStr(const char8_t *inName) override + { + qt3ds::foundation::CRegisteredString theName(Register(inName)); + Q3DStudio::ERuntimeDataModelDataType theType( + m_MetaData.GetPropertyType(m_Type, theName, m_ClassId)); + if (theType != Q3DStudio::ERuntimeDataModelDataTypeObjectRef + && theType != Q3DStudio::ERuntimeDataModelDataTypeLong4) { + return m_MetaData.GetPropertyValueString(m_Type, theName, m_ClassId); + } + return Empty(); + } + Option<QT3DSF32> ParseFloat(const char8_t *inName) override + { + return m_MetaData.GetPropertyValueFloat(m_Type, Register(inName), m_ClassId); + } + Option<QT3DSVec2> ParseVec2(const char8_t *inName) override + { + Option<qt3ds::QT3DSVec2> theProperty = + m_MetaData.GetPropertyValueVector2(m_Type, Register(inName), m_ClassId); + if (theProperty.hasValue()) + return *theProperty; + + return Empty(); + } + Option<QT3DSVec3> ParseVec3(const char8_t *inName) override + { + Option<qt3ds::QT3DSVec3> theProperty = + m_MetaData.GetPropertyValueVector3(m_Type, Register(inName), m_ClassId); + if (theProperty.hasValue()) { + return *theProperty; + } + return Empty(); + } + Option<QT3DSVec4> ParseVec4(const char8_t *inName) override + { + Option<qt3ds::QT3DSVec4> theProperty = + m_MetaData.GetPropertyValueVector4(m_Type, Register(inName), m_ClassId); + if (theProperty.hasValue()) { + return *theProperty; + } + return Empty(); + } + Option<bool> ParseBool(const char8_t *inName) override + { + return m_MetaData.GetPropertyValueBool(m_Type, Register(inName), m_ClassId); + } + + Option<QT3DSU32> ParseU32(const char8_t *inName) override + { + Option<QT3DSI32> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId); + if (retval.hasValue()) + return (QT3DSU32)retval.getValue(); + return Empty(); + } + + Option<QT3DSI32> ParseI32(const char8_t *inName) override + { + Option<QT3DSI32> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId); + if (retval.hasValue()) + return (QT3DSI32)retval.getValue(); + return Empty(); + } + + Option<SGraphObject *> ParseGraphObject(const char8_t *) override { return Empty(); } + Option<SNode *> ParseNode(const char8_t *) override { return Empty(); } +}; + +class IDOMReferenceResolver +{ +protected: + virtual ~IDOMReferenceResolver() {} +public: + virtual SGraphObject *ResolveReference(SGraphObject &inRootObject, const char *path) = 0; +}; + +struct SDomReaderPropertyParser : public IPropertyParser +{ + qt3dsdm::IDOMReader &m_Reader; + nvvector<char8_t> &m_TempBuf; + IDOMReferenceResolver &m_Resolver; + SGraphObject &m_Object; + + SDomReaderPropertyParser(qt3dsdm::IDOMReader &reader, nvvector<char8_t> &inTempBuf, + IDOMReferenceResolver &inResolver, SGraphObject &inObject) + : m_Reader(reader) + , m_TempBuf(inTempBuf) + , m_Resolver(inResolver) + , m_Object(inObject) + { + } + Option<TStrType> ParseStr(const char8_t *inName) override + { + const char8_t *retval; + if (m_Reader.Att(inName, retval)) + return TStrType(retval); + return Empty(); + } + Option<QT3DSF32> ParseFloat(const char8_t *inName) override + { + QT3DSF32 retval; + if (m_Reader.Att(inName, retval)) + return retval; + return Empty(); + } + Option<QT3DSVec2> ParseVec2(const char8_t *inName) override + { + qt3dsdm::SFloat2 retval; + const char8_t *tempData; + if (m_Reader.UnregisteredAtt(inName, tempData)) { + qt3dsdm::WStrOps<qt3dsdm::SFloat2>().StrTo(tempData, retval, m_TempBuf); + return QT3DSVec2(retval.m_Floats[0], retval.m_Floats[1]); + } + return Empty(); + } + Option<QT3DSVec3> ParseVec3(const char8_t *inName) override + { + qt3dsdm::SFloat3 retval; + const char8_t *tempData; + if (m_Reader.UnregisteredAtt(inName, tempData)) { + qt3dsdm::WStrOps<qt3dsdm::SFloat3>().StrTo(tempData, retval, m_TempBuf); + return QT3DSVec3(retval.m_Floats[0], retval.m_Floats[1], retval.m_Floats[2]); + } + return Empty(); + } + Option<QT3DSVec4> ParseVec4(const char8_t *inName) override + { + qt3dsdm::SFloat4 retval; + const char8_t *tempData; + if (m_Reader.UnregisteredAtt(inName, tempData)) { + qt3dsdm::WStrOps<qt3dsdm::SFloat4>().StrTo(tempData, retval, m_TempBuf); + return QT3DSVec4(retval.m_Floats[0], retval.m_Floats[1], retval.m_Floats[2], + retval.m_Floats[3]); + } + return Empty(); + } + Option<bool> ParseBool(const char8_t *inName) override + { + bool retval; + if (m_Reader.Att(inName, retval)) + return retval; + return Empty(); + } + + Option<QT3DSU32> ParseU32(const char8_t *inName) override + { + QT3DSU32 retval; + if (m_Reader.Att(inName, retval)) + return retval; + return Empty(); + } + + Option<QT3DSI32> ParseI32(const char8_t *inName) override + { + QT3DSI32 retval; + if (m_Reader.Att(inName, retval)) + return retval; + return Empty(); + } + + Option<SGraphObject *> ParseGraphObject(const char8_t *inName) override + { + const char *temp; + if (m_Reader.UnregisteredAtt(inName, temp)) { + // Now we need to figure out if this is an element reference or if it is a relative path + // from the current element. + SGraphObject *retval = m_Resolver.ResolveReference(m_Object, temp); + if (retval) + return retval; + } + return Empty(); + } + + Option<SNode *> ParseNode(const char8_t *inName) override + { + Option<SGraphObject *> obj = ParseGraphObject(inName); + if (obj.hasValue()) { + if (GraphObjectTypes::IsNodeType((*obj)->m_Type)) + return static_cast<SNode *>((*obj)); + } + return Empty(); + } +}; + +template <typename TDataType> +struct SParserHelper +{ +}; +template <> +struct SParserHelper<TStrType> +{ + static Option<TStrType> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseStr(inName); + } +}; +template <> +struct SParserHelper<QT3DSF32> +{ + static Option<QT3DSF32> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseFloat(inName); + } +}; +template <> +struct SParserHelper<QT3DSVec2> +{ + static Option<QT3DSVec2> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseVec2(inName); + } +}; +template <> +struct SParserHelper<QT3DSVec3> +{ + static Option<QT3DSVec3> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseVec3(inName); + } +}; +template <> +struct SParserHelper<QT3DSVec4> +{ + static Option<QT3DSVec4> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseVec4(inName); + } +}; +template <> +struct SParserHelper<bool> +{ + static Option<bool> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseBool(inName); + } +}; +template <> +struct SParserHelper<QT3DSU32> +{ + static Option<QT3DSU32> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseU32(inName); + } +}; +template <> +struct SParserHelper<QT3DSI32> +{ + static Option<QT3DSI32> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseI32(inName); + } +}; +template <> +struct SParserHelper<SGraphObject *> +{ + static Option<SGraphObject *> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseGraphObject(inName); + } +}; +template <> +struct SParserHelper<SNode *> +{ + static Option<SNode *> Parse(const char8_t *inName, IPropertyParser &inParser) + { + return inParser.ParseNode(inName); + } +}; + +struct SPathAndAnchorIndex +{ + SPathSubPath *m_Segment; + QT3DSU32 m_AnchorIndex; + SPathAndAnchorIndex(SPathSubPath *inSegment, QT3DSU32 inAnchorIndex) + : m_Segment(inSegment) + , m_AnchorIndex(inAnchorIndex) + { + } + SPathAndAnchorIndex() + : m_Segment(NULL) + , m_AnchorIndex(0) + { + } +}; + +struct SRenderUIPLoader : public IDOMReferenceResolver +{ + typedef qt3dsdm::IDOMReader::Scope TScope; + typedef eastl::map<CRegisteredString, eastl::string> TIdStringMap; + typedef eastl::hash_map<CRegisteredString, SPathAndAnchorIndex> TIdPathAnchorIndexMap; + qt3dsdm::IDOMReader &m_Reader; + Q3DStudio::IRuntimeMetaData &m_MetaData; + IStringTable &m_StrTable; + NVFoundationBase &m_Foundation; + NVAllocatorCallback &m_PresentationAllocator; + qt3ds::render::TIdObjectMap &m_ObjectMap; + IBufferManager &m_BufferManager; + SPresentation *m_Presentation; + nvvector<char8_t> m_TempBuf; + TStrType m_TempParseString; + IEffectSystem &m_EffectSystem; + const char8_t *m_PresentationDir; + Qt3DSString m_PathString; + qt3ds::render::IRenderPluginManager &m_RenderPluginManager; + qt3ds::render::ICustomMaterialSystem &m_CustomMaterialSystem; + qt3ds::render::IDynamicObjectSystem &m_DynamicObjectSystem; + qt3ds::render::IPathManager &m_PathManager; + TIdStringMap m_RenderPluginSourcePaths; + IUIPReferenceResolver *m_ReferenceResolver; + MemoryBuffer<RawAllocator> m_TempBuffer; + MemoryBuffer<RawAllocator> m_ValueBuffer; + TIdPathAnchorIndexMap m_AnchorIdToPathAndAnchorIndexMap; + const Q3DSVariantConfig &m_variantConfig; + + SRenderUIPLoader(qt3dsdm::IDOMReader &inReader, const char8_t *inFullPathToPresentationFile, + Q3DStudio::IRuntimeMetaData &inMetaData, IStringTable &inStrTable + // Allocator for datastructures we need to parse the file. + , + NVFoundationBase &inFoundation + // Allocator used for the presentation objects themselves + , + NVAllocatorCallback &inPresentationAllocator + // Map of string ids to objects + , + TIdObjectMap &ioObjectMap, IBufferManager &inBufferManager, + IEffectSystem &inEffectSystem, const char8_t *inPresentationDir, + qt3ds::render::IRenderPluginManager &inRPM, + qt3ds::render::ICustomMaterialSystem &inCMS, + qt3ds::render::IDynamicObjectSystem &inDynamicSystem, + qt3ds::render::IPathManager &inPathManager, IUIPReferenceResolver *inResolver, + const Q3DSVariantConfig &variantConfig) + : m_Reader(inReader) + , m_MetaData(inMetaData) + , m_StrTable(inStrTable) + , m_Foundation(inFoundation) + , m_PresentationAllocator(inPresentationAllocator) + , m_ObjectMap(ioObjectMap) + , m_BufferManager(inBufferManager) + , m_Presentation(QT3DS_NEW(inPresentationAllocator, SPresentation)()) + , m_TempBuf(inFoundation.getAllocator(), "SRenderUIPLoader::m_TempBuf") + , m_EffectSystem(inEffectSystem) + , m_PresentationDir(inPresentationDir) + , m_RenderPluginManager(inRPM) + , m_CustomMaterialSystem(inCMS) + , m_DynamicObjectSystem(inDynamicSystem) + , m_PathManager(inPathManager) + , m_ReferenceResolver(inResolver) + , m_variantConfig(variantConfig) + { + std::string presentationFile = inFullPathToPresentationFile; + std::string::size_type pos = presentationFile.find_last_of("\\/"); + if (pos != std::string::npos) { + std::string path = presentationFile.substr(0, pos); + m_Presentation->m_PresentationDirectory = inStrTable.RegisterStr(path.c_str()); + } + } + + SGraphObject *ResolveReference(SGraphObject &inRoot, const char *path) override + { + if (m_ReferenceResolver) { + CRegisteredString resolvedReference = + m_ReferenceResolver->ResolveReference(inRoot.m_Id, path); + if (resolvedReference.IsValid()) { + qt3ds::render::TIdObjectMap::iterator iter = m_ObjectMap.find(resolvedReference); + if (iter != m_ObjectMap.end()) + return iter->second; + } + } + return NULL; + } + + static bool IsNode(GraphObjectTypes::Enum inType) + { + return GraphObjectTypes::IsNodeType(inType); + } + template <typename TDataType> + bool ParseProperty(IPropertyParser &inParser, const char8_t *inName, TDataType &outData) + { + Option<TDataType> theValue(SParserHelper<TDataType>::Parse(inName, inParser)); + if (theValue.hasValue()) { + outData = theValue; + return true; + } + return false; + } + bool ParseOpacityProperty(IPropertyParser &inParser, const char8_t *inName, QT3DSF32 &outOpacity) + { + if (ParseProperty(inParser, inName, outOpacity)) { + outOpacity /= 100.0f; + return true; + } + return false; + } + + bool ParseRadianProperty(IPropertyParser &inParser, const char8_t *inName, QT3DSVec3 &ioRotation) + { + if (ParseProperty(inParser, inName, ioRotation)) { + TORAD(ioRotation.x); + TORAD(ioRotation.y); + TORAD(ioRotation.z); + return true; + } + return false; + } + bool ParseRadianProperty(IPropertyParser &inParser, const char8_t *inName, QT3DSF32 &ioRotation) + { + if (ParseProperty(inParser, inName, ioRotation)) { + TORAD(ioRotation); + return true; + } + return false; + } + + void ParseRotationOrder(IPropertyParser &inParser, const char8_t *inName, + QT3DSU32 &ioRotationOrder) + { + if (ParseProperty(inParser, inName, m_TempParseString)) + ioRotationOrder = qt3ds::render::MapRotationOrder(m_TempParseString.c_str()); + } + void ParseOrientation(IPropertyParser &inParser, const char8_t *inName, NodeFlags &ioFlags) + { + if (ParseProperty(inParser, inName, m_TempParseString)) { + if (m_TempParseString == "Left Handed") + ioFlags.SetLeftHanded(true); + else + ioFlags.SetLeftHanded(false); + } + } + void ParseOrthographicProperty(IPropertyParser &inParser, const char8_t *inName, + NodeFlags &ioFlags) + { + bool isOrthographic; + if (ParseProperty(inParser, inName, isOrthographic)) + ioFlags.SetOrthographic(isOrthographic); + } + template <typename TEnumType> + static bool ConvertEnumFromStr(const char8_t *inStr, TEnumType &ioEnum) + { + qt3ds::render::SEnumNameMap *theMap = qt3ds::render::SEnumParseMap<TEnumType>::GetMap(); + for (qt3ds::render::SEnumNameMap *item = theMap; item->m_Name; ++item) { + // hack to match advanced overlay types, whose name start with a '*' + const char8_t *p = inStr; + if (*p == '*') + ++p; + if (qt3dsdm::AreEqual(p, item->m_Name)) { + ioEnum = static_cast<TEnumType>(item->m_Enum); + return true; + } + } + return false; + } + + template <typename TEnumType> + void ParseEnumProperty(IPropertyParser &inParser, const char8_t *inName, TEnumType &ioEnum) + { + if (ParseProperty(inParser, inName, m_TempParseString)) { + ConvertEnumFromStr(m_TempParseString.c_str(), ioEnum); + } + } + void ParseAndResolveSourcePath(IPropertyParser &inParser, const char8_t *inName, + CRegisteredString &ioString) + { + if (ParseProperty(inParser, inName, m_TempParseString)) + ioString = m_StrTable.RegisterStr(m_TempParseString.c_str()); + } + void ParseProperty(IPropertyParser &inParser, const char8_t *inName, SImage *&ioImage) + { + if (ParseProperty(inParser, inName, m_TempParseString)) { + TIdObjectMap::iterator theIter = + m_ObjectMap.find(m_StrTable.RegisterStr(m_TempParseString.c_str() + 1)); + if (theIter != m_ObjectMap.end() + && theIter->second->m_Type == GraphObjectTypes::Image) { + ioImage = static_cast<SImage *>(theIter->second); + } else { + QT3DS_ASSERT(false); + } + } + } + void ParseProperty(IPropertyParser &inParser, const char8_t *inName, CRegisteredString &ioStr) + { + if (ParseProperty(inParser, inName, m_TempParseString)) + ioStr = m_StrTable.RegisterStr(m_TempParseString.c_str()); + } + + void ParseNodeFlagsProperty(IPropertyParser &inParser, const char8_t *inName, + qt3ds::render::NodeFlags &ioFlags, + qt3ds::render::NodeFlagValues::Enum prop) + { + bool temp; + if (ParseProperty(inParser, inName, temp)) + ioFlags.ClearOrSet(temp, prop); + } + + void ParseNodeFlagsInverseProperty(IPropertyParser &inParser, const char8_t *inName, + qt3ds::render::NodeFlags &ioFlags, + qt3ds::render::NodeFlagValues::Enum prop) + { + bool temp; + if (ParseProperty(inParser, inName, temp)) + ioFlags.ClearOrSet(!temp, prop); + } + +// Create a mapping from UICRenderPropertyNames to the string in the UIP file. +#define Scene_ClearColor "backgroundcolor" +#define Scene_UseClearColor "bgcolorenable" +#define Node_Rotation "rotation" +#define Node_Position "position" +#define Node_Scale "scale" +#define Node_Pivot "pivot" +#define Node_LocalOpacity "opacity" +#define Node_RotationOrder "rotationorder" +#define Node_LeftHanded "orientation" +#define Layer_Variants "variants" +#define Layer_TemporalAAEnabled "temporalaa" +#define Layer_LayerEnableDepthTest "disabledepthtest" +#define Layer_LayerEnableDepthPrePass "disabledepthprepass" +#define Layer_ClearColor "backgroundcolor" +#define Layer_Background "background" +#define Layer_BlendType "blendtype" +#define Layer_Size "size" +#define Layer_Location "location" +#define Layer_TexturePath "sourcepath" +#define Layer_HorizontalFieldValues "horzfields" +#define Layer_Left "left" +#define Layer_LeftUnits "leftunits" +#define Layer_Width "width" +#define Layer_WidthUnits "widthunits" +#define Layer_Right "right" +#define Layer_RightUnits "rightunits" +#define Layer_VerticalFieldValues "vertfields" +#define Layer_Top "top" +#define Layer_TopUnits "topunits" +#define Layer_Height "height" +#define Layer_HeightUnits "heightunits" +#define Layer_Bottom "bottom" +#define Layer_BottomUnits "bottomunits" +#define Layer_AoStrength "aostrength" +#define Layer_AoDistance "aodistance" +#define Layer_AoSoftness "aosoftness" +#define Layer_AoBias "aobias" +#define Layer_AoSamplerate "aosamplerate" +#define Layer_AoDither "aodither" +#define Layer_ShadowStrength "shadowstrength" +#define Layer_ShadowDist "shadowdist" +#define Layer_ShadowSoftness "shadowsoftness" +#define Layer_ShadowBias "shadowbias" +#define Layer_LightProbe "lightprobe" +#define Layer_ProbeBright "probebright" +#define Layer_FastIbl "fastibl" +#define Layer_ProbeHorizon "probehorizon" +#define Layer_ProbeFov "probefov" +#define Layer_LightProbe2 "lightprobe2" +#define Layer_Probe2Fade "probe2fade" +#define Layer_Probe2Window "probe2window" +#define Layer_Probe2Pos "probe2pos" +#define Camera_ClipNear "clipnear" +#define Camera_ClipFar "clipfar" +#define Camera_FOV "fov" +#define Camera_FOVHorizontal "fovhorizontal" +#define Camera_Orthographic "orthographic" +#define Camera_ScaleMode "scalemode" +#define Camera_ScaleAnchor "scaleanchor" +#define Light_LightType "lighttype" +#define Light_DiffuseColor "lightdiffuse" +#define Light_SpecularColor "lightspecular" +#define Light_AmbientColor "lightambient" +#define Light_Brightness "brightness" +#define Light_LinearFade "linearfade" +#define Light_ExponentialFade "expfade" +#define Light_AreaWidth "areawidth" +#define Light_AreaHeight "areaheight" +#define Light_CastShadow "castshadow" +#define Light_ShadowBias "shdwbias" +#define Light_ShadowFactor "shdwfactor" +#define Light_ShadowMapRes "shdwmapres" +#define Light_ShadowMapFar "shdwmapfar" +#define Light_ShadowMapFov "shdwmapfov" +#define Light_ShadowFilter "shdwfilter" +#define Model_MeshPath "sourcepath" +#define Model_ShadowCaster "shadowcaster" +#define Model_TessellationMode "tessellation" +#define Model_EdgeTess "edgetess" +#define Model_InnerTess "innertess" +#define Lightmaps_LightmapIndirect "lightmapindirect" +#define Lightmaps_LightmapRadiosity "lightmapradiosity" +#define Lightmaps_LightmapShadow "lightmapshadow" +#define Material_Lighting "shaderlighting" +#define Material_BlendMode "blendmode" +#define MaterialBase_IblProbe "iblprobe" +#define Material_DiffuseColor "diffuse" +#define Material_DiffuseMaps_0 "diffusemap" +#define Material_DiffuseMaps_1 "diffusemap2" +#define Material_DiffuseMaps_2 "diffusemap3" +#define Material_EmissivePower "emissivepower" +#define Material_EmissiveColor "emissivecolor" +#define Material_EmissiveMap "emissivemap" +#define Material_EmissiveMap2 "emissivemap2" +#define Material_SpecularReflection "specularreflection" +#define Material_SpecularMap "specularmap" +#define Material_SpecularModel "specularmodel" +#define Material_SpecularTint "speculartint" +#define Material_IOR "ior" +#define Material_FresnelPower "fresnelPower" +#define Material_SpecularAmount "specularamount" +#define Material_SpecularRoughness "specularroughness" +#define Material_RoughnessMap "roughnessmap" +#define Material_Opacity "opacity" +#define Material_OpacityMap "opacitymap" +#define Material_BumpMap "bumpmap" +#define Material_BumpAmount "bumpamount" +#define Material_NormalMap "normalmap" +#define Material_DisplacementMap "displacementmap" +#define Material_DisplaceAmount "displaceamount" +#define Material_TranslucencyMap "translucencymap" +#define Material_TranslucentFalloff "translucentfalloff" +#define Material_DiffuseLightWrap "diffuselightwrap" +#define Material_ReferencedMaterial "referencedmaterial" +#define Material_VertexColors "vertexcolors" +#define Image_ImagePath "sourcepath" +#define Image_OffscreenRendererId "subpresentation" +#define Image_Scale_X "scaleu" +#define Image_Scale_Y "scalev" +#define Image_Pivot_X "pivotu" +#define Image_Pivot_Y "pivotv" +#define Image_Rotation "rotationuv" +#define Image_Position_X "positionu" +#define Image_Position_Y "positionv" +#define Image_MappingMode "mappingmode" +#define Image_HorizontalTilingMode "tilingmodehorz" +#define Image_VerticalTilingMode "tilingmodevert" +#define Text_Text "textstring" +#define Text_Font "font" +#define Text_FontSize "size" +#define Text_HorizontalAlignment "horzalign" +#define Text_VerticalAlignment "vertalign" +#define Text_Leading "leading" +#define Text_Tracking "tracking" +#define Text_DropShadow "dropshadow" +#define Text_DropShadowStrength "dropshadowstrength" +#define Text_DropShadowOffsetX "dropshadowoffsetx" +#define Text_DropShadowOffsetY "dropshadowoffsety" +#define Text_WordWrap "wordwrap" +#define Text_BoundingBox "boundingbox" +#define Text_Elide "elide" +#define Text_TextColor "textcolor" +#define Text_BackColor "backcolor" +#define Text_EnableAcceleratedFont "enableacceleratedfont" +#define Layer_ProgressiveAAMode "progressiveaa" +#define Layer_MultisampleAAMode "multisampleaa" +#define Light_Scope "scope" +#define Path_PathType "pathtype" +#define Path_PaintStyle "paintstyle" +#define Path_Width "width" +#define Path_Opacity "opacity" +#define Path_LinearError "linearerror" +#define Path_EdgeTessAmount "edgetessamount" +#define Path_InnerTessAmount "innertessamount" +#define Path_BeginCapping "begincap" +#define Path_BeginCapOffset "begincapoffset" +#define Path_BeginCapOpacity "begincapopacity" +#define Path_BeginCapWidth "begincapwidth" +#define Path_EndCapping "endcap" +#define Path_EndCapOffset "endcapoffset" +#define Path_EndCapOpacity "endcapopacity" +#define Path_EndCapWidth "endcapwidth" +#define Path_PathBuffer "sourcepath" +#define SubPath_Closed "closed" + +// Fill in implementations for the actual parse tables. +#define HANDLE_QT3DS_RENDER_PROPERTY(type, name, dirty) \ + ParseProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_REAL_VEC2_PROPERTY(type, name, dirty) \ + ParseProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_VEC3_PROPERTY(type, name, dirty) \ + ParseProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_COLOR_PROPERTY(type, name, dirty) \ + ParseProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_RADIAN_PROPERTY(type, name, dirty) \ + ParseRadianProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_VEC3_RADIAN_PROPERTY(type, name, dirty) \ + ParseRadianProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_OPACITY_PROPERTY(type, name, dirty) \ + ParseOpacityProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_ROTATION_ORDER_PROPERTY(type, name, dirty) \ + ParseRotationOrder(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_NODE_ORIENTATION_PROPERTY(type, name, dirty) \ + ParseOrientation(inParser, type##_##name, inItem.m_Flags); +#define HANDLE_QT3DS_RENDER_DEPTH_TEST_PROPERTY(type, name, dirty) \ + if (ParseProperty(inParser, type##_##name, inItem.m_##name)) \ + inItem.m_##name = !inItem.m_##name; +#define HANDLE_QT3DS_NODE_FLAGS_PROPERTY(type, name, dirty) \ + ParseNodeFlagsProperty(inParser, type##_##name, inItem.m_Flags, \ + qt3ds::render::NodeFlagValues::name); +#define HANDLE_QT3DS_NODE_FLAGS_INVERSE_PROPERTY(type, name, dirty) \ + ParseNodeFlagsInverseProperty(inParser, type##_##name, inItem.m_Flags, \ + qt3ds::render::NodeFlagValues::name); +#define HANDLE_QT3DS_RENDER_ENUM_PROPERTY(type, name, dirty) \ + ParseEnumProperty(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(type, name, dirty) \ + ParseAndResolveSourcePath(inParser, type##_##name, inItem.m_##name); +#define HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(type, name, index, dirty) \ + ParseProperty(inParser, type##_##name##_##index, inItem.m_##name[index]); +#define HANDLE_QT3DS_RENDER_VEC2_PROPERTY(type, name, dirty) \ + ParseProperty(inParser, type##_##name##_##X, inItem.m_##name.x); \ + ParseProperty(inParser, type##_##name##_##Y, inItem.m_##name.y); +#define HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY( \ + type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_COLOR_PROPERTY +#define HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY( \ + type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_VEC3_PROPERTY + + // Call the correct parser functions. + void ParseProperties(SScene &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_SCENE_PROPERTIES + } + void ParseProperties(SNode &inItem, IPropertyParser &inParser) + { + bool eyeball; + if (ParseProperty(inParser, "eyeball", eyeball)) + inItem.m_Flags.SetActive(eyeball); + ITERATE_QT3DS_RENDER_NODE_PROPERTIES + ParseProperty(inParser, "boneid", inItem.m_SkeletonId); + bool ignoreParent = false; + if (ParseProperty(inParser, "ignoresparent", ignoreParent)) + inItem.m_Flags.SetIgnoreParentTransform(ignoreParent); + } + void ParseProperties(SLayer &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_LAYER_PROPERTIES + ParseProperty(inParser, "aosamplerate", inItem.m_AoSamplerate); + } + void ParseProperties(SCamera &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_CAMERA_PROPERTIES + } + void ParseProperties(SLight &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_LIGHT_PROPERTIES + ParseProperty(inParser, "shdwmapres", inItem.m_ShadowMapRes); + } + void ParseProperties(SModel &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_MODEL_PROPERTIES + ParseProperty(inParser, "poseroot", inItem.m_SkeletonRoot); + } + + void ParseProperties(SText &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_TEXT_PROPERTIES + } + void ParseProperties(SLightmaps &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_LIGHTMAP_PROPERTIES + } + void ParseProperties(SDefaultMaterial &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_MATERIAL_PROPERTIES + ParseProperties(inItem.m_Lightmaps, inParser); + } + void ParseProperties(SReferencedMaterial &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_REFERENCED_MATERIAL_PROPERTIES + // Propagate lightmaps + if (inItem.m_ReferencedMaterial + && inItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::DefaultMaterial) + ParseProperties( + static_cast<SDefaultMaterial *>(inItem.m_ReferencedMaterial)->m_Lightmaps, + inParser); + else if (inItem.m_ReferencedMaterial + && inItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::CustomMaterial) + ParseProperties( + static_cast<SCustomMaterial *>(inItem.m_ReferencedMaterial)->m_Lightmaps, inParser); + } + void ParseProperties(SImage &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_IMAGE_PROPERTIES + } + template <typename TDataType> + void SetDynamicObjectProperty(SDynamicObject &inEffect, + const qt3ds::render::dynamic::SPropertyDefinition &inPropDesc, + const TDataType &inProp) + { + memCopy(inEffect.GetDataSectionBegin() + inPropDesc.m_Offset, &inProp, sizeof(TDataType)); + } + template <typename TDataType> + void SetDynamicObjectProperty(SDynamicObject &inEffect, + const qt3ds::render::dynamic::SPropertyDefinition &inPropDesc, + Option<TDataType> inProp) + { + if (inProp.hasValue()) { + SetDynamicObjectProperty(inEffect, inPropDesc, *inProp); + } + } + void ParseProperties(SCustomMaterial &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SDynamicObject &>(inItem), inParser); + ParseProperties(inItem.m_Lightmaps, inParser); + ITERATE_QT3DS_RENDER_CUSTOM_MATERIAL_PROPERTIES + } + void ParseProperties(SDynamicObject &inDynamicObject, IPropertyParser &inParser) + { + NVConstDataRef<qt3ds::render::dynamic::SPropertyDefinition> theProperties = + m_DynamicObjectSystem.GetProperties(inDynamicObject.m_ClassName); + + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + const qt3ds::render::dynamic::SPropertyDefinition &theDefinition(theProperties[idx]); + switch (theDefinition.m_DataType) { + case qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool: + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseBool(theDefinition.m_Name)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSF32: + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseFloat(theDefinition.m_Name)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSI32: + if (theDefinition.m_IsEnumProperty == false) + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseU32(theDefinition.m_Name)); + else { + Option<eastl::string> theEnum = inParser.ParseStr(theDefinition.m_Name); + if (theEnum.hasValue()) { + NVConstDataRef<CRegisteredString> theEnumNames = + theDefinition.m_EnumValueNames; + for (QT3DSU32 idx = 0, end = theEnumNames.size(); idx < end; ++idx) { + if (theEnum->compare(theEnumNames[idx].c_str()) == 0) { + SetDynamicObjectProperty(inDynamicObject, theDefinition, idx); + break; + } + } + } + } + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4: + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseVec4(theDefinition.m_Name)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3: + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseVec3(theDefinition.m_Name)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2: + SetDynamicObjectProperty(inDynamicObject, theDefinition, + inParser.ParseVec2(theDefinition.m_Name)); + break; + case qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr: + case qt3ds::render::NVRenderShaderDataTypes::NVRenderImage2DPtr: { + Option<eastl::string> theTexture = inParser.ParseStr(theDefinition.m_Name); + if (theTexture.hasValue()) { + CRegisteredString theStr; + if (theTexture->size()) + theStr = m_StrTable.RegisterStr(theTexture->c_str()); + + SetDynamicObjectProperty(inDynamicObject, theDefinition, theStr); + } + } break; + case qt3ds::render::NVRenderShaderDataTypes::NVRenderDataBufferPtr: + break; + default: + QT3DS_ASSERT(false); + break; + } + } + } + void ParseProperties(SPath &inItem, IPropertyParser &inParser) + { + ParseProperties(static_cast<SNode &>(inItem), inParser); + ITERATE_QT3DS_RENDER_PATH_PROPERTIES + } + void ParseProperties(SPathSubPath &inItem, IPropertyParser &inParser) + { + ITERATE_QT3DS_RENDER_PATH_SUBPATH_PROPERTIES + } + + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<float> data) + { + if (data.hasValue()) { + ioUpdates.push_back( + qt3ds::render::SRenderPropertyValueUpdate(inDeclaration.m_Name, *data)); + } + } + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &inClass, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<QT3DSVec2> data) + { + if (data.hasValue()) { + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(inDeclaration.m_StartOffset).first, data->x)); + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(inDeclaration.m_StartOffset + 1).first, data->y)); + } + } + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &inClass, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<QT3DSVec3> data) + { + if (data.hasValue()) { + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(inDeclaration.m_StartOffset).first, data->x)); + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(inDeclaration.m_StartOffset + 1).first, data->y)); + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(inDeclaration.m_StartOffset + 2).first, data->z)); + } + } + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<qt3ds::QT3DSI32> dataOpt) + { + if (dataOpt.hasValue()) { + long data = static_cast<long>(*dataOpt); + ioUpdates.push_back( + qt3ds::render::SRenderPropertyValueUpdate(inDeclaration.m_Name, (QT3DSI32)data)); + } + } + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<eastl::string> dataOpt) + { + if (dataOpt.hasValue()) { + eastl::string &data = dataOpt.getValue(); + ioUpdates.push_back(qt3ds::render::SRenderPropertyValueUpdate( + inDeclaration.m_Name, m_StrTable.RegisterStr(data.c_str()))); + } + } + void AddPluginPropertyUpdate(eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> &ioUpdates, + qt3ds::render::IRenderPluginClass &, + const qt3ds::render::SRenderPluginPropertyDeclaration &inDeclaration, + Option<bool> dataOpt) + { + if (dataOpt.hasValue()) { + bool &data = dataOpt.getValue(); + ioUpdates.push_back( + qt3ds::render::SRenderPropertyValueUpdate(inDeclaration.m_Name, data)); + } + } + void ParseProperties(qt3ds::render::SRenderPlugin &inRenderPlugin, IPropertyParser &inParser) + { + qt3ds::render::IRenderPluginClass *theClass = + m_RenderPluginManager.GetRenderPlugin(inRenderPlugin.m_PluginPath); + if (theClass) { + qt3ds::foundation::NVConstDataRef<qt3ds::render::SRenderPluginPropertyDeclaration> + theClassProps = theClass->GetRegisteredProperties(); + if (theClassProps.size()) { + qt3ds::render::IRenderPluginInstance *theInstance = + m_RenderPluginManager.GetOrCreateRenderPluginInstance( + inRenderPlugin.m_PluginPath, &inRenderPlugin); + if (theInstance) { + eastl::vector<qt3ds::render::SRenderPropertyValueUpdate> theUpdates; + for (QT3DSU32 idx = 0, end = theClassProps.size(); idx < end; ++idx) { + const qt3ds::render::SRenderPluginPropertyDeclaration &theDec( + theClassProps[idx]); + eastl::string tempStr; + switch (theDec.m_Type) { + case qt3ds::render::SRenderPluginPropertyTypes::Float: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseFloat(theDec.m_Name.c_str())); + break; + case qt3ds::render::SRenderPluginPropertyTypes::Vector2: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseVec2(theDec.m_Name.c_str())); + break; + case qt3ds::render::SRenderPluginPropertyTypes::Color: + case qt3ds::render::SRenderPluginPropertyTypes::Vector3: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseVec3(theDec.m_Name.c_str())); + break; + case qt3ds::render::SRenderPluginPropertyTypes::Long: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseI32(theDec.m_Name.c_str())); + break; + case qt3ds::render::SRenderPluginPropertyTypes::String: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseStr(theDec.m_Name.c_str())); + break; + case qt3ds::render::SRenderPluginPropertyTypes::Boolean: + AddPluginPropertyUpdate(theUpdates, *theClass, theDec, + inParser.ParseBool(theDec.m_Name.c_str())); + break; + default: + QT3DS_ASSERT(false); + } + } + theInstance->Update( + qt3ds::foundation::toConstDataRef(theUpdates.data(), theUpdates.size())); + } + } + } + } + +#undef HANDLE_QT3DS_RENDER_PROPERTY +#undef HANDLE_QT3DS_RENDER_ENUM_PROPERTY +#undef HANDLE_QT3DS_RENDER_RADIAN_PROPERTY +#undef HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY +#undef HANDLE_QT3DS_RENDER_ARRAY_PROPERTY +#undef HANDLE_QT3DS_NODE_FLAGS_PROPERTY +#undef HANDLE_QT3DS_ROTATION_ORDER_PROPERTY +#undef HANDLE_QT3DS_RENDER_OPACITY_PROPERTY +#undef HANDLE_QT3DS_NODE_ORIENTATION_PROPERTY +#undef HANDLE_QT3DS_RENDER_DEPTH_TEST_PROPERTY +#undef HANDLE_QT3DS_RENDER_VEC2_PROPERTY + + void ParseGraphPass1(SGraphObject *inParent) + { + TScope __elemScope(m_Reader); + qt3dsdm::ComposerObjectTypes::Enum theObjType = + qt3dsdm::ComposerObjectTypes::Convert(m_Reader.GetElementName()); + SGraphObject *theNewObject(NULL); + const char8_t *theId; + const char8_t *theVariants; + m_Reader.Att("id", theId); + m_Reader.Att("variants", theVariants); + + QString theString(theVariants); + QStringRef theStringRef(&theString); + bool isPartOfConfig = m_variantConfig.isPartOfConfig(theStringRef); + if (isPartOfConfig) { + switch (theObjType) { + case qt3dsdm::ComposerObjectTypes::Scene: { + SScene *theScene = QT3DS_NEW(m_PresentationAllocator, SScene)(); + theNewObject = theScene; + m_Presentation->m_Scene = theScene; + theScene->m_Presentation = m_Presentation; + } break; + case qt3dsdm::ComposerObjectTypes::Layer: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SLayer)(); + break; + case qt3dsdm::ComposerObjectTypes::Group: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SNode)(); + break; + case qt3dsdm::ComposerObjectTypes::Component: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SNode)(); + break; + case qt3dsdm::ComposerObjectTypes::Camera: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SCamera)(); + break; + case qt3dsdm::ComposerObjectTypes::Light: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SLight)(); + break; + case qt3dsdm::ComposerObjectTypes::Model: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SModel)(); + break; + case qt3dsdm::ComposerObjectTypes::Material: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SDefaultMaterial)(); + break; + case qt3dsdm::ComposerObjectTypes::ReferencedMaterial: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SReferencedMaterial)(); + break; + case qt3dsdm::ComposerObjectTypes::Image: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SImage)(); + break; + case qt3dsdm::ComposerObjectTypes::Text: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SText)(); + break; + case qt3dsdm::ComposerObjectTypes::Path: + theNewObject = QT3DS_NEW(m_PresentationAllocator, SPath)(); + break; + case qt3dsdm::ComposerObjectTypes::SubPath: { + SPathSubPath *thePath = QT3DS_NEW(m_PresentationAllocator, SPathSubPath)(); + theNewObject = thePath; + QT3DSU32 anchorCount = 0; + TScope _childScope(m_Reader); + for (bool success = m_Reader.MoveToFirstChild("PathAnchorPoint"); success; + success = m_Reader.MoveToNextSibling("PathAnchorPoint")) { + const char8_t *theId; + m_Reader.Att("id", theId); + CRegisteredString theIdStr = m_StrTable.RegisterStr(theId); + m_AnchorIdToPathAndAnchorIndexMap.insert( + eastl::make_pair(theIdStr, SPathAndAnchorIndex(thePath, anchorCount))); + ++anchorCount; + } + m_PathManager.ResizePathSubPathBuffer(*thePath, anchorCount); + } break; + case qt3dsdm::ComposerObjectTypes::Effect: { + const char8_t *effectClassId; + m_Reader.Att("class", effectClassId); + CRegisteredString theStr = m_StrTable.RegisterStr(effectClassId + 1); + if (m_EffectSystem.IsEffectRegistered(theStr)) + theNewObject = m_EffectSystem.CreateEffectInstance(theStr, m_PresentationAllocator); + } break; + case qt3dsdm::ComposerObjectTypes::RenderPlugin: { + const char8_t *classId; + m_Reader.Att("class", classId); + if (!qt3ds::foundation::isTrivial(classId)) { + ++classId; + TIdStringMap::iterator iter = + m_RenderPluginSourcePaths.find(m_StrTable.RegisterStr(classId)); + if (iter != m_RenderPluginSourcePaths.end()) { + CRegisteredString thePluginPath = m_StrTable.RegisterStr(iter->second.c_str()); + qt3ds::render::IRenderPluginClass *theClass = + m_RenderPluginManager.GetRenderPlugin(thePluginPath); + if (theClass) { + qt3ds::render::SRenderPlugin *thePlugin = + QT3DS_NEW(m_PresentationAllocator, qt3ds::render::SRenderPlugin)(); + thePlugin->m_PluginPath = thePluginPath; + thePlugin->m_Flags.SetActive(true); + theNewObject = thePlugin; + } + } + } + } break; + case qt3dsdm::ComposerObjectTypes::CustomMaterial: { + const char8_t *materialClassId; + m_Reader.Att("class", materialClassId); + CRegisteredString theStr = m_StrTable.RegisterStr(materialClassId + 1); + if (m_CustomMaterialSystem.IsMaterialRegistered(theStr)) { + theNewObject = + m_CustomMaterialSystem.CreateCustomMaterial(theStr, m_PresentationAllocator); + } + } break; + default: + // Ignoring unknown objects entirely at this point + break; + } + } + if (theNewObject) { + CRegisteredString theObjectId(m_StrTable.RegisterStr(theId)); + m_ObjectMap.insert(eastl::make_pair(theObjectId, theNewObject)); + theNewObject->m_Id = theObjectId; + // setup hierarchy + bool isParentNode; + bool isChildNode; + if (inParent) { + switch (inParent->m_Type) { + case GraphObjectTypes::Scene: + if (theNewObject->m_Type == GraphObjectTypes::Layer) { + static_cast<SScene *>(inParent)->AddChild( + *static_cast<SLayer *>(theNewObject)); + } + break; + + case GraphObjectTypes::DefaultMaterial: + if (theNewObject->m_Type == GraphObjectTypes::Image) { + static_cast<SImage *>(theNewObject)->m_Parent = + static_cast<SDefaultMaterial *>(inParent); + eastl::string thePath = eastl::string(theNewObject->m_Id.c_str()); + if (thePath.find("probe") != eastl::string::npos) + static_cast<SImage *>(theNewObject)->m_MappingMode = + ImageMappingModes::LightProbe; + } + break; + + case GraphObjectTypes::CustomMaterial: + if (theNewObject->m_Type == GraphObjectTypes::Image) { + static_cast<SImage *>(theNewObject)->m_Parent = + static_cast<SCustomMaterial *>(inParent); + eastl::string thePath = eastl::string(theNewObject->m_Id.c_str()); + if (thePath.find("probe") != eastl::string::npos) { + static_cast<SImage *>(theNewObject)->m_MappingMode = + ImageMappingModes::LightProbe; + } + } else { + QT3DS_ASSERT(false); + } + break; + case GraphObjectTypes::ReferencedMaterial: + if (theNewObject->m_Type == GraphObjectTypes::Image) { + // nothing to do yet + } else { + QT3DS_ASSERT(false); + } + break; + case GraphObjectTypes::Path: + + if (GraphObjectTypes::IsMaterialType(theNewObject->m_Type)) + static_cast<SPath *>(inParent)->AddMaterial(theNewObject); + + else if (theNewObject->m_Type == GraphObjectTypes::PathSubPath) + static_cast<SPath *>(inParent)->AddSubPath( + *static_cast<SPathSubPath *>(theNewObject)); + + break; + + default: + isParentNode = IsNode(inParent->m_Type); + isChildNode = IsNode(theNewObject->m_Type); + if (isParentNode && isChildNode) { + static_cast<SNode *>(inParent)->AddChild( + *static_cast<SNode *>(theNewObject)); + } else if (isParentNode) { + if (inParent->m_Type == GraphObjectTypes::Model + && IsMaterial(theNewObject)) { + static_cast<SModel *>(inParent)->AddMaterial(*theNewObject); + } else { + if (inParent->m_Type == GraphObjectTypes::Layer + && theNewObject->m_Type == GraphObjectTypes::Effect) { + static_cast<SLayer *>(inParent)->AddEffect( + *static_cast<SEffect *>(theNewObject)); + } else if (inParent->m_Type == GraphObjectTypes::Layer + && theNewObject->m_Type == GraphObjectTypes::Image) { + eastl::string thePath = eastl::string(theNewObject->m_Id.c_str()); + if (thePath.find("probe2") != eastl::string::npos) { + static_cast<SLayer *>(inParent)->m_LightProbe2 = + static_cast<SImage *>(theNewObject); + } else { + static_cast<SLayer *>(inParent)->m_LightProbe = + static_cast<SImage *>(theNewObject); + } + } else { + if (theNewObject->m_Type == GraphObjectTypes::RenderPlugin) { + qt3ds::render::SRenderPlugin *childObj = + static_cast<qt3ds::render::SRenderPlugin *>(theNewObject); + if (inParent->m_Type == GraphObjectTypes::Layer) { + static_cast<SLayer *>(inParent)->m_RenderPlugin = childObj; + } else { + QT3DS_ASSERT(false); + } + } else { + QT3DS_ASSERT(false); + } + } + } + } else { + if (inParent->m_Type == GraphObjectTypes::Image + && theNewObject->m_Type == GraphObjectTypes::RenderPlugin) { + static_cast<SImage *>(inParent)->m_RenderPlugin = + static_cast<qt3ds::render::SRenderPlugin *>(theNewObject); + } else { + QT3DS_ASSERT(false); + } + } + } + } + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) + ParseGraphPass1(theNewObject); + } else { + if (isPartOfConfig) { + // Object was of unknown type -> parse children with NULL parent + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) { + ParseGraphPass1(NULL); + } + } + // If object wasn't part of variant config -> skip children. + // Continue parsing from next sibling with same parent. + } + } + + template <typename TObjType> + void ParsePass2Properties(TObjType &inObject, const char8_t *inClassId) + { + const char8_t *theTypeName = m_Reader.GetNarrowElementName(); + SMetaPropertyParser theMetaParser(theTypeName, inClassId, m_MetaData); + // Set default values + ParseProperties(inObject, theMetaParser); + + // Now setup property values from the element itself. + SDomReaderPropertyParser theReaderParser(m_Reader, m_TempBuf, *this, inObject); + ParseProperties(inObject, theReaderParser); + } + + // Parse the instance properties from the graph. + void ParseGraphPass2() + { + TScope __instanceScope(m_Reader); + const char8_t *theId; + m_Reader.Att("id", theId); + const char8_t *theClass = ""; + const char8_t *theVariants = ""; + m_Reader.Att("class", theClass); + m_Reader.Att("variants", theVariants); + + QString theString(theVariants); + QStringRef theStringRef(&theString); + bool isPartOfConfig = m_variantConfig.isPartOfConfig(theStringRef); + if (isPartOfConfig) { + TIdObjectMap::iterator theObject = m_ObjectMap.find(m_StrTable.RegisterStr(theId)); + if (theObject != m_ObjectMap.end()) { + switch (theObject->second->m_Type) { + case GraphObjectTypes::Scene: + ParsePass2Properties(*static_cast<SScene *>(theObject->second), theClass); + break; + case GraphObjectTypes::Node: + ParsePass2Properties(*static_cast<SNode *>(theObject->second), theClass); + break; + case GraphObjectTypes::Layer: + ParsePass2Properties(*static_cast<SLayer *>(theObject->second), theClass); + break; + case GraphObjectTypes::Camera: + ParsePass2Properties(*static_cast<SCamera *>(theObject->second), theClass); + break; + case GraphObjectTypes::Light: + ParsePass2Properties(*static_cast<SLight *>(theObject->second), theClass); + break; + case GraphObjectTypes::Model: + ParsePass2Properties(*static_cast<SModel *>(theObject->second), theClass); + break; + case GraphObjectTypes::DefaultMaterial: + ParsePass2Properties(*static_cast<SDefaultMaterial *>(theObject->second), theClass); + break; + case GraphObjectTypes::ReferencedMaterial: + ParsePass2Properties(*static_cast<SReferencedMaterial *>(theObject->second), + theClass); + break; + case GraphObjectTypes::Image: + ParsePass2Properties(*static_cast<SImage *>(theObject->second), theClass); + break; + case GraphObjectTypes::Text: + ParsePass2Properties(*static_cast<SText *>(theObject->second), theClass); + break; + case GraphObjectTypes::Effect: + ParsePass2Properties(*static_cast<SEffect *>(theObject->second), theClass); + break; + case GraphObjectTypes::RenderPlugin: + ParsePass2Properties(*static_cast<qt3ds::render::SRenderPlugin *>(theObject->second), + theClass); + break; + case GraphObjectTypes::CustomMaterial: + ParsePass2Properties(*static_cast<SCustomMaterial *>(theObject->second), theClass); + break; + case GraphObjectTypes::Path: + ParsePass2Properties(*static_cast<SPath *>(theObject->second), theClass); + break; + case GraphObjectTypes::PathSubPath: + ParsePass2Properties(*static_cast<SPathSubPath *>(theObject->second), theClass); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + } + + // If not part of variant config -> ignore children + if (isPartOfConfig) { + for (bool valid = m_Reader.MoveToFirstChild(); valid; valid = m_Reader.MoveToNextSibling()) + ParseGraphPass2(); + } + } + + static bool ParseVec2(SDomReaderPropertyParser &inParser, const char *inName, QT3DSVec2 &outValue) + { + Option<QT3DSVec2> result = inParser.ParseVec2(inName); + + if (result.hasValue()) + outValue = *result; + + return result.hasValue(); + } + + static bool ParseFloat(SDomReaderPropertyParser &inParser, const char *inName, QT3DSF32 &outValue) + { + Option<QT3DSF32> result = inParser.ParseFloat(inName); + if (result.hasValue()) + outValue = *result; + return result.hasValue(); + } + + void ParseState(bool inSetSetValues) + { + TScope __slideScope(m_Reader); + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) { + if (strcmp(m_Reader.GetNarrowElementName(), "Add") == 0 + || (inSetSetValues && strcmp(m_Reader.GetNarrowElementName(), "Set") == 0)) { + const char8_t *theId; + m_Reader.Att("ref", theId); + CRegisteredString theIdStr(m_StrTable.RegisterStr(theId + 1)); + if (m_ObjectMap.contains(theIdStr)) { + TIdObjectMap::iterator theObject = m_ObjectMap.find(theIdStr); + if (theObject != m_ObjectMap.end()) { + SDomReaderPropertyParser parser(m_Reader, m_TempBuf, *this, *theObject->second); + switch (theObject->second->m_Type) { + case GraphObjectTypes::Scene: + ParseProperties(*reinterpret_cast<SScene *>(theObject->second), parser); + break; + case GraphObjectTypes::Node: + ParseProperties(*reinterpret_cast<SNode *>(theObject->second), parser); + break; + case GraphObjectTypes::Layer: + ParseProperties(*reinterpret_cast<SLayer *>(theObject->second), parser); + break; + case GraphObjectTypes::Camera: + ParseProperties(*reinterpret_cast<SCamera *>(theObject->second), parser); + break; + case GraphObjectTypes::Light: + ParseProperties(*reinterpret_cast<SLight *>(theObject->second), parser); + break; + case GraphObjectTypes::Model: + ParseProperties(*reinterpret_cast<SModel *>(theObject->second), parser); + break; + case GraphObjectTypes::DefaultMaterial: + ParseProperties(*reinterpret_cast<SDefaultMaterial *>(theObject->second), + parser); + break; + case GraphObjectTypes::ReferencedMaterial: + ParseProperties(*static_cast<SReferencedMaterial *>(theObject->second), + parser); + break; + case GraphObjectTypes::Image: + ParseProperties(*reinterpret_cast<SImage *>(theObject->second), parser); + break; + case GraphObjectTypes::Text: + ParseProperties(*static_cast<SText *>(theObject->second), parser); + break; + case GraphObjectTypes::Effect: + ParseProperties(*static_cast<SEffect *>(theObject->second), parser); + break; + case GraphObjectTypes::RenderPlugin: + ParseProperties( + *static_cast<qt3ds::render::SRenderPlugin *>(theObject->second), parser); + break; + case GraphObjectTypes::CustomMaterial: + ParseProperties( + *static_cast<qt3ds::render::SCustomMaterial *>(theObject->second), + parser); + break; + case GraphObjectTypes::Path: + ParseProperties(*static_cast<qt3ds::render::SPath *>(theObject->second), + parser); + break; + case GraphObjectTypes::PathSubPath: + ParseProperties( + *static_cast<qt3ds::render::SPathSubPath *>(theObject->second), parser); + break; + default: + QT3DS_ASSERT(false); + break; + } + } else { + TIdPathAnchorIndexMap::iterator iter = + m_AnchorIdToPathAndAnchorIndexMap.find(theIdStr); + if (iter != m_AnchorIdToPathAndAnchorIndexMap.end()) { + SDomReaderPropertyParser parser(m_Reader, m_TempBuf, *this, + *iter->second.m_Segment); + NVDataRef<qt3ds::render::SPathAnchorPoint> thePathBuffer = + m_PathManager.GetPathSubPathBuffer(*iter->second.m_Segment); + QT3DSU32 anchorIndex = iter->second.m_AnchorIndex; + QT3DSU32 numAnchors = thePathBuffer.size(); + if (anchorIndex < numAnchors) { + qt3ds::render::SPathAnchorPoint &thePoint(thePathBuffer[anchorIndex]); + ParseVec2(parser, "position", thePoint.m_Position); + ParseFloat(parser, "incomingangle", thePoint.m_IncomingAngle); + thePoint.m_OutgoingAngle = thePoint.m_IncomingAngle + 180.0f; + ParseFloat(parser, "incomingdistance", thePoint.m_IncomingDistance); + ParseFloat(parser, "outgoingdistance", thePoint.m_OutgoingDistance); + } + } + } + } + } + } + } + + void AddPluginProperty(qt3ds::render::IRenderPluginClass &pluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Enum inPropType, + eastl::string &tempStr, const char *propName) + { + tempStr.assign(propName); + qt3ds::render::SRenderPluginPropertyDeclaration theDec( + m_StrTable.RegisterStr(tempStr.c_str()), inPropType); + pluginClass.RegisterProperty(theDec); + } + + SPresentation *Load(bool inSetValuesFromSlides) + { + { + TScope __outerScope(m_Reader); + if (m_Reader.MoveToFirstChild("ProjectSettings")) { + m_Reader.Att("presentationWidth", m_Presentation->m_PresentationDimensions.x); + m_Reader.Att("presentationHeight", m_Presentation->m_PresentationDimensions.y); + // Upsize them to a multiple of four. + m_Presentation->m_PresentationDimensions.x = + (QT3DSF32)qt3ds::render::ITextRenderer::NextMultipleOf4( + (QT3DSU32)m_Presentation->m_PresentationDimensions.x); + m_Presentation->m_PresentationDimensions.y = + (QT3DSF32)qt3ds::render::ITextRenderer::NextMultipleOf4( + (QT3DSU32)m_Presentation->m_PresentationDimensions.y); + const char8_t *thePresentationRotation = ""; + if (m_Reader.Att("presentationRotation", thePresentationRotation)) { + bool success = SRenderUIPLoader::ConvertEnumFromStr( + thePresentationRotation, m_Presentation->m_PresentationRotation); + (void)success; + QT3DS_ASSERT(success); + } + m_Reader.Att("preferKTX", m_Presentation->m_preferKTX); + } + } + { + TScope __outerScope(m_Reader); + if (m_Reader.MoveToFirstChild("Classes")) { + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) { + const char8_t *idStr = "", *name = "", *sourcepath = ""; + m_Reader.Att("id", idStr); + m_Reader.Att("name", name); + m_Reader.Att("sourcepath", sourcepath); + if (AreEqual(m_Reader.GetNarrowElementName(), "Effect")) { + CRegisteredString theId(m_StrTable.RegisterStr(idStr)); + if (m_EffectSystem.IsEffectRegistered(theId) == false) { + // File should already be loaded. + Option<qt3dsdm::SMetaDataEffect> theEffectMetaData = + m_MetaData.GetEffectMetaDataBySourcePath(sourcepath); + if (theEffectMetaData.hasValue()) { + qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect( + theId, m_Foundation, m_EffectSystem, *theEffectMetaData, + m_StrTable); + } else { + QT3DS_ASSERT(false); + } + } + } else if (AreEqual(m_Reader.GetNarrowElementName(), "CustomMaterial")) { + CRegisteredString theId(m_StrTable.RegisterStr(idStr)); + if (m_CustomMaterialSystem.IsMaterialRegistered(theId) == false) { + // File should already be loaded. + Option<qt3dsdm::SMetaDataCustomMaterial> theMetaData = + m_MetaData.GetMaterialMetaDataBySourcePath(sourcepath); + if (theMetaData.hasValue()) { + qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( + theId, m_Foundation, m_CustomMaterialSystem, *theMetaData, + m_StrTable); + } else { + QT3DS_ASSERT(false); + } + } + } else if (AreEqual(m_Reader.GetNarrowElementName(), "RenderPlugin")) { + CRegisteredString theId(m_StrTable.RegisterStr(idStr)); + m_MetaData.LoadPluginXMLFile(m_Reader.GetNarrowElementName(), idStr, name, + sourcepath); + eastl::vector<Q3DStudio::TRuntimeMetaDataStrType> theProperties; + qt3ds::render::IRenderPluginClass *thePluginClass = + m_RenderPluginManager.GetOrCreateRenderPlugin( + m_StrTable.RegisterStr(sourcepath)); + if (thePluginClass) { + m_RenderPluginSourcePaths.insert( + eastl::make_pair(m_StrTable.RegisterStr(idStr), sourcepath)); + m_MetaData.GetInstanceProperties(m_Reader.GetNarrowElementName(), idStr, + theProperties, false); + eastl::string thePropertyStr; + CRegisteredString metaType = + m_MetaData.GetStringTable()->GetRenderStringTable().RegisterStr( + m_Reader.GetNarrowElementName()); + CRegisteredString metaId = + m_MetaData.GetStringTable()->GetRenderStringTable().RegisterStr( + idStr); + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + using namespace Q3DStudio; + CRegisteredString metaProp = + m_MetaData.GetStringTable()->GetRenderStringTable().RegisterStr( + theProperties[idx].c_str()); + Q3DStudio::ERuntimeDataModelDataType thePropType = + m_MetaData.GetPropertyType(metaType, metaProp, metaId); + switch (thePropType) { + case ERuntimeDataModelDataTypeFloat: + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Float, + thePropertyStr, metaProp.c_str()); + break; + case ERuntimeDataModelDataTypeFloat2: + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Vector2, + thePropertyStr, metaProp.c_str()); + break; + case ERuntimeDataModelDataTypeFloat3: + if (m_MetaData.GetAdditionalType(metaType, metaProp, metaId) + != ERuntimeAdditionalMetaDataTypeColor) + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Vector3, + thePropertyStr, metaProp.c_str()); + else + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Color, + thePropertyStr, metaProp.c_str()); + break; + case ERuntimeDataModelDataTypeLong: + AddPluginProperty(*thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Long, + thePropertyStr, metaProp.c_str()); + break; + case ERuntimeDataModelDataTypeString: + case ERuntimeDataModelDataTypeStringRef: + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::String, + thePropertyStr, metaProp.c_str()); + break; + case ERuntimeDataModelDataTypeBool: + AddPluginProperty( + *thePluginClass, + qt3ds::render::SRenderPluginPropertyTypes::Boolean, + thePropertyStr, metaProp.c_str()); + break; + default: + QT3DS_ASSERT(false); + } + } + } + } + } + } + } + { + TScope __outerScope(m_Reader); + if (m_Reader.MoveToFirstChild("BufferData")) { + { + TScope __imageScope(m_Reader); + for (bool valid = m_Reader.MoveToFirstChild("ImageBuffer"); valid; + valid = m_Reader.MoveToNextSibling()) { + const char8_t *srcPath; + m_Reader.UnregisteredAtt("sourcepath", srcPath); + CRegisteredString imgPath = m_StrTable.RegisterStr(srcPath); + bool hasTransparency = false; + m_Reader.Att("hasTransparency", hasTransparency); + m_BufferManager.SetImageHasTransparency(imgPath, hasTransparency); + } + } + } + } + { + TScope __outerScope(m_Reader); + { + if (m_Reader.MoveToFirstChild("Graph")) { + { + TScope __graphScope(m_Reader); + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) + ParseGraphPass1(NULL); + } + { + TScope __graphScope(m_Reader); + for (bool valid = m_Reader.MoveToFirstChild(); valid; + valid = m_Reader.MoveToNextSibling()) + ParseGraphPass2(); + } + } + } + } + TScope __outerScope(m_Reader); + if (m_Reader.MoveToFirstChild("Logic")) { + for (bool valid = m_Reader.MoveToFirstChild("State"); valid; + valid = m_Reader.MoveToNextSibling()) { + { + TScope __slideScope(m_Reader); + ParseState(true); // parse master + for (bool subSlide = m_Reader.MoveToFirstChild("State"); subSlide; + subSlide = m_Reader.MoveToNextSibling("State")) { + TScope __subSlideScope(m_Reader); + ParseState(false); // parse slide setting only *add* values + } + } + { + TScope __slideScope(m_Reader); + if (inSetValuesFromSlides && m_Reader.MoveToFirstChild("State")) + ParseState(true); // parse slide setting only *set* values + } + } + } + + return m_Presentation; + } +}; +} + +SPresentation *qt3ds::render::IUIPLoader::LoadUIPFile( + qt3dsdm::IDOMReader &inReader, const char8_t *inFullPathToPresentationFile, + Q3DStudio::IRuntimeMetaData &inMetaData, IStringTable &inStrTable, + NVFoundationBase &inFoundation + // Allocator used for the presentation objects themselves + // this allows clients to pre-allocate a block of memory just for + // the scene graph + , + NVAllocatorCallback &inPresentationAllocator + // Map of string ids to objects + , + TIdObjectMap &ioObjectMap, IBufferManager &inBufferManager, IEffectSystem &inEffectSystem, + const char8_t *inPresentationDir, IRenderPluginManager &inPluginManager, + ICustomMaterialSystem &inCMS, IDynamicObjectSystem &inDynamicSystem, + qt3ds::render::IPathManager &inPathManager, IUIPReferenceResolver *inResolver, + const Q3DSVariantConfig &variantConfig, bool inSetValuesFromSlides) +{ + SRenderUIPLoader theLoader(inReader, inFullPathToPresentationFile, inMetaData, inStrTable, + inFoundation, inPresentationAllocator, ioObjectMap, inBufferManager, + inEffectSystem, inPresentationDir, inPluginManager, inCMS, + inDynamicSystem, inPathManager, inResolver, variantConfig); + return theLoader.Load(inSetValuesFromSlides); +} +using namespace qt3dsdm; + +inline qt3ds::render::NVRenderTextureFormats::Enum +ConvertTypeAndFormatToTextureFormat(const char8_t *inType, const char8_t *inFormat, + NVFoundationBase &inFoundation) +{ + qt3ds::render::NVRenderTextureFormats::Enum retval = qt3ds::render::NVRenderTextureFormats::RGBA8; + if (AreEqual(inType, "ubyte")) { + if (AreEqual(inFormat, "rgb")) + retval = qt3ds::render::NVRenderTextureFormats::RGB8; + else if (AreEqual(inFormat, "rgba")) + retval = qt3ds::render::NVRenderTextureFormats::RGBA8; + else if (AreEqual(inFormat, "alpha")) + retval = qt3ds::render::NVRenderTextureFormats::Alpha8; + else if (AreEqual(inFormat, "lum")) + retval = qt3ds::render::NVRenderTextureFormats::Luminance8; + else if (AreEqual(inFormat, "lum_alpha")) + retval = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8; + } else if (AreEqual(inType, "ushort")) { + if (AreEqual(inFormat, "rgb")) + retval = qt3ds::render::NVRenderTextureFormats::RGB565; + else if (AreEqual(inFormat, "rgba")) + retval = qt3ds::render::NVRenderTextureFormats::RGBA5551; + } else { + qCCritical(INVALID_PARAMETER, "Unsupported texture type %s, defaulting to RGBA8", + inType); + } + return retval; +} + +inline qt3ds::render::NVRenderTextureMagnifyingOp::Enum +ConvertFilterToMagOp(const char8_t *inFilter, NVFoundationBase &inFoundation) +{ + if (AreEqual(inFilter, "linear")) + return qt3ds::render::NVRenderTextureMagnifyingOp::Linear; + if (AreEqual(inFilter, "nearest")) + return qt3ds::render::NVRenderTextureMagnifyingOp::Nearest; + else { + qCCritical(INVALID_PARAMETER, "Unsupported filter type %s, defaulting to linear", + inFilter); + return qt3ds::render::NVRenderTextureMagnifyingOp::Linear; + } +} + +inline qt3ds::render::NVRenderTextureCoordOp::Enum +ConvertTextureCoordOp(const char8_t *inWrap, NVFoundationBase &inFoundation) +{ + if (AreEqual(inWrap, "clamp")) + return qt3ds::render::NVRenderTextureCoordOp::ClampToEdge; + if (AreEqual(inWrap, "repeat")) + return qt3ds::render::NVRenderTextureCoordOp::Repeat; + else { + qCCritical(INVALID_PARAMETER, "Unsupported wrap type %s, defaulting to clamp", + inWrap); + return qt3ds::render::NVRenderTextureCoordOp::ClampToEdge; + } +} + +// Re-register all strings because we can't be sure that the meta data system and the effect +// system are sharing the same string table. +void qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect( + CRegisteredString inEffectName, NVFoundationBase &inFoundation, IEffectSystem &inEffectSystem, + const qt3dsdm::SMetaDataEffect &inMetaDataEffect, IStringTable &inStrTable) +{ + using namespace qt3ds::render::dynamic; + if (inEffectSystem.IsEffectRegistered(inEffectName)) { + qCCritical(INVALID_OPERATION, "Effect %s is already registered", + inEffectName.c_str()); + QT3DS_ASSERT(false); + return; + } + nvvector<SPropertyDeclaration> thePropertyDeclarations( + inFoundation.getAllocator(), "qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect"); + nvvector<CRegisteredString> theEnumNames( + inFoundation.getAllocator(), "qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect"); + Qt3DSString theConvertStr; + Qt3DSString theConvertShaderTypeStr; + Qt3DSString theConvertShaderVersionStr; + + for (QT3DSU32 idx = 0, end = inMetaDataEffect.m_Properties.size(); idx < end; ++idx) + thePropertyDeclarations.push_back( + SPropertyDeclaration(inMetaDataEffect.m_Properties[idx].m_Name.c_str(), + inMetaDataEffect.m_Properties[idx].m_DataType)); + inEffectSystem.RegisterEffect(inEffectName, thePropertyDeclarations); + for (QT3DSU32 idx = 0, end = inMetaDataEffect.m_Properties.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(inMetaDataEffect.m_Properties[idx]); + if (theDefinition.m_EnumValueNames.size()) { + theEnumNames.clear(); + for (QT3DSU32 enumIdx = 0, enumEnd = theDefinition.m_EnumValueNames.size(); + enumIdx < enumEnd; ++enumIdx) + theEnumNames.push_back( + inStrTable.RegisterStr(theDefinition.m_EnumValueNames[enumIdx])); + inEffectSystem.SetEffectPropertyEnumNames( + inEffectName, inStrTable.RegisterStr(theDefinition.m_Name), theEnumNames); + } + if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr) + inEffectSystem.SetEffectPropertyTextureSettings( + inEffectName, inStrTable.RegisterStr(theDefinition.m_Name), + inStrTable.RegisterStr(theDefinition.m_ImagePath), theDefinition.m_TexUsageType, + theDefinition.m_CoordOp, theDefinition.m_MagFilterOp, theDefinition.m_MinFilterOp); + } + for (QT3DSU32 idx = 0, end = inMetaDataEffect.m_Shaders.size(); idx < end; ++idx) { + const qt3dsdm::SMetaDataShader &theShader = inMetaDataEffect.m_Shaders[idx]; + theConvertStr.clear(); + theConvertStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Code.c_str()); + theConvertShaderTypeStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Type.c_str()); + theConvertShaderVersionStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Version.c_str()); + + inEffectSystem.SetShaderData(inStrTable.RegisterStr(theShader.m_Name.c_str()), + theConvertStr.c_str(), theConvertShaderVersionStr.c_str(), + theConvertStr.c_str(), theShader.m_HasGeomShader, + theShader.m_IsComputeShader); + } + + inEffectSystem.SetEffectCommands(inEffectName, inMetaDataEffect.m_EffectCommands); +} + +void qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( + CRegisteredString inClassName, NVFoundationBase &inFoundation, + ICustomMaterialSystem &inMaterialSystem, + const qt3dsdm::SMetaDataCustomMaterial &inMetaDataMaterial, IStringTable &inStrTable) +{ + using namespace qt3ds::render::dynamic; + if (inMaterialSystem.IsMaterialRegistered(inClassName)) { + qCCritical(INVALID_OPERATION, "Effect %s is already registered", + inClassName.c_str()); + QT3DS_ASSERT(false); + return; + } + nvvector<SPropertyDeclaration> thePropertyDeclarations( + inFoundation.getAllocator(), + "qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial"); + nvvector<CRegisteredString> theEnumNames( + inFoundation.getAllocator(), + "qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial"); + Qt3DSString theConvertStr; + Qt3DSString theConvertShaderTypeStr; + Qt3DSString theConvertShaderVersionStr; + for (QT3DSU32 idx = 0, end = inMetaDataMaterial.m_Properties.size(); idx < end; ++idx) + thePropertyDeclarations.push_back( + SPropertyDeclaration(inMetaDataMaterial.m_Properties[idx].m_Name.c_str(), + inMetaDataMaterial.m_Properties[idx].m_DataType)); + inMaterialSystem.RegisterMaterialClass(inClassName, thePropertyDeclarations); + for (QT3DSU32 idx = 0, end = inMetaDataMaterial.m_Properties.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(inMetaDataMaterial.m_Properties[idx]); + if (theDefinition.m_EnumValueNames.size()) { + theEnumNames.clear(); + for (QT3DSU32 enumIdx = 0, enumEnd = theDefinition.m_EnumValueNames.size(); + enumIdx < enumEnd; ++enumIdx) + theEnumNames.push_back( + inStrTable.RegisterStr(theDefinition.m_EnumValueNames[enumIdx])); + inMaterialSystem.SetPropertyEnumNames( + inClassName, inStrTable.RegisterStr(theDefinition.m_Name), theEnumNames); + } + if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr) + inMaterialSystem.SetPropertyTextureSettings( + inClassName, inStrTable.RegisterStr(theDefinition.m_Name), + inStrTable.RegisterStr(theDefinition.m_ImagePath), theDefinition.m_TexUsageType, + theDefinition.m_CoordOp, theDefinition.m_MagFilterOp, theDefinition.m_MinFilterOp); + } + if (inMetaDataMaterial.m_Shaders.size()) { + for (QT3DSU32 idx = 0, end = (QT3DSU32)inMetaDataMaterial.m_Shaders.size(); idx < end; ++idx) { + const qt3dsdm::SMetaDataShader &theShader = inMetaDataMaterial.m_Shaders[idx]; + theConvertStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Code.c_str()); + theConvertShaderTypeStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Type.c_str()); + theConvertShaderVersionStr = Qt3DSStringUtils::ConvertUTFtoQString( + theShader.m_Version.c_str()); + inMaterialSystem.SetMaterialClassShader( + inStrTable.RegisterStr(theShader.m_Name.c_str()), theConvertShaderTypeStr.c_str(), + theConvertShaderVersionStr.c_str(), theConvertStr.c_str(), + theShader.m_HasGeomShader, theShader.m_IsComputeShader); + } + } + + inMaterialSystem.SetCustomMaterialCommands(inClassName, + inMetaDataMaterial.m_CustomMaterialCommands); + inMaterialSystem.SetCustomMaterialTransparency(inClassName, + inMetaDataMaterial.m_HasTransparency); + inMaterialSystem.SetCustomMaterialRefraction(inClassName, inMetaDataMaterial.m_HasRefraction); + inMaterialSystem.SetCustomMaterialAlwaysDirty(inClassName, inMetaDataMaterial.m_AlwaysDirty); + inMaterialSystem.SetCustomMaterialShaderKey(inClassName, inMetaDataMaterial.m_ShaderKey); + inMaterialSystem.SetCustomMaterialLayerCount(inClassName, inMetaDataMaterial.m_LayerCount); +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderUIPLoader.h b/src/runtimerender/Qt3DSRenderUIPLoader.h new file mode 100644 index 0000000..2b70c68 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderUIPLoader.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_UIP_LOADER_H +#define QT3DS_RENDER_UIP_LOADER_H + +#ifdef QT3DS_RENDER_ENABLE_LOAD_UIP + +#include "Qt3DSRender.h" +#include "foundation/StringTable.h" +#include <EASTL/utility.h> +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderGraphObject.h" +#include <QtCore/qstring.h> + +namespace Q3DStudio { +class IRuntimeMetaData; +} + +namespace qt3dsdm { +class IDOMReader; +struct SMetaDataEffect; +struct SMetaDataCustomMaterial; +} + +namespace qt3ds { +class Q3DSVariantConfig; + +namespace render { + + class IBufferManager; + + typedef nvhash_map<CRegisteredString, SGraphObject *> TIdObjectMap; + + struct IUIPReferenceResolver + { + protected: + virtual ~IUIPReferenceResolver() {} + public: + virtual CRegisteredString ResolveReference(CRegisteredString inStart, + const char *inReference) = 0; + }; + + struct SPresentation; + + class QT3DS_AUTOTEST_EXPORT IUIPLoader + { + public: + // The reader needs to point to the top of the file, we will search + // several objects that exist at the top level of the uip file. + // Returns NULL if we were incapable of loading the presentation. + static SPresentation * + LoadUIPFile(qt3dsdm::IDOMReader &inReader + // the full path, including the filename + // to the presentation file + , + const char8_t *inFullPathToPresentationFile, + Q3DStudio::IRuntimeMetaData &inMetaData, IStringTable &inStrTable, + NVFoundationBase &inFoundation + // Allocator used for the presentation objects themselves + // this allows clients to pre-allocate a block of memory just for + // the scene graph + , + NVAllocatorCallback &inPresentationAllocator + // Map of string ids to objects + , + TIdObjectMap &ioObjectMap + // Buffer manager to load details about the images + , + IBufferManager &inBufferManager + // To load effects we need the effect system + // and the presentation directory + , + IEffectSystem &inEffectSystem, const char8_t *inPresentationDir, + IRenderPluginManager &inPluginManager, ICustomMaterialSystem &inMaterialSystem, + IDynamicObjectSystem &inDynamicSystem, qt3ds::render::IPathManager &inPathManager + // Resolve references to objects; this is done by the main uip loader during + // its normal mode of operation so we try to reuse that code. + , + IUIPReferenceResolver *inResolver + // Variant config defines variant groups and tags to be used to filter out + // unneeded parts of the presentation + , + const Q3DSVariantConfig &variantConfig + // Set some initial values by going to the master slide then slide 1 + // Useful for quick testing, sort of equivalent to showing the first frame + // of a given presentation + , + bool setValuesFromSlides = false); + + static void CreateEffectClassFromMetaEffect(CRegisteredString inEffectName, + NVFoundationBase &inFoundation, + IEffectSystem &inEffectSystem, + const qt3dsdm::SMetaDataEffect &inMetaDataEffect, + IStringTable &inStrTable); + + static void CreateMaterialClassFromMetaMaterial( + CRegisteredString inEffectName, NVFoundationBase &inFoundation, + ICustomMaterialSystem &inEffectSystem, + const qt3dsdm::SMetaDataCustomMaterial &inMetaDataMaterial, IStringTable &inStrTable); + }; +} +} + +#endif +#endif diff --git a/src/runtimerender/Qt3DSRenderUIPSharedTranslation.cpp b/src/runtimerender/Qt3DSRenderUIPSharedTranslation.cpp new file mode 100644 index 0000000..a059493 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderUIPSharedTranslation.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderUIPSharedTranslation.h" + +namespace qt3ds { +namespace render { + +#define WCHAR_T_Directional L"Directional" +#define WCHAR_T_Point L"Point" +#define WCHAR_T_Area L"Area" +#define WCHAR_T_None L"None" +#define WCHAR_T_Vertex L"Vertex" +#define WCHAR_T_Pixel L"Pixel" +#define WCHAR_T_Normal L"Normal" +#define WCHAR_T_Screen L"Screen" +#define WCHAR_T_Multiply L"Multiply" +#define WCHAR_T_Overlay L"Overlay" +#define WCHAR_T_ColorBurn L"ColorBurn" +#define WCHAR_T_ColorDodge L"ColorDodge" +#define WCHAR_T_Add L"Add" +#define WCHAR_T_Subtract L"Subtract" +#define WCHAR_T_UV_Mapping L"UV Mapping" +#define WCHAR_T_Environmental_Mapping L"Environmental Mapping" +#define WCHAR_T_Light_Probe L"Light Probe" +#define WCHAR_T_No_Tiling L"No Tiling" +#define WCHAR_T_Mirrored L"Mirrored" +#define WCHAR_T_Tiled L"Tiled" +#define WCHAR_T_Left L"Left" +#define WCHAR_T_Center L"Center" +#define WCHAR_T_Right L"Right" +#define WCHAR_T_Top L"Top" +#define WCHAR_T_Middle L"Middle" +#define WCHAR_T_Bottom L"Bottom" +#define WCHAR_T_ElideNone L"ElideNone" +#define WCHAR_T_ElideLeft L"ElideLeft" +#define WCHAR_T_ElideMiddle L"ElideMiddle" +#define WCHAR_T_ElideRight L"ElideRight" +#define WCHAR_T_2x L"2x" +#define WCHAR_T_4x L"4x" +#define WCHAR_T_8x L"8x" +#define WCHAR_T_SSAA L"SSAA" +#define WCHAR_T_NoRotation L"NoRotation" +#define WCHAR_T_Clockwise90 L"90" +#define WCHAR_T_Clockwise180 L"180" +#define WCHAR_T_Clockwise270 L"270" +#define WCHAR_T_Fit L"Fit" +#define WCHAR_T_Same_Size L"Same Size" +#define WCHAR_T_CENTER L"Center" +#define WCHAR_T_North L"N" +#define WCHAR_T_NorthEast L"NE" +#define WCHAR_T_East L"E" +#define WCHAR_T_SouthEast L"SE" +#define WCHAR_T_South L"S" +#define WCHAR_T_SouthWest L"SW" +#define WCHAR_T_West L"W" +#define WCHAR_T_NorthWest L"NW" +#define WCHAR_T_LeftWidth L"Left/Width" +#define WCHAR_T_LeftRight L"Left/Right" +#define WCHAR_T_WidthRight L"Width/Right" +#define WCHAR_T_TopHeight L"Top/Height" +#define WCHAR_T_TopBottom L"Top/Bottom" +#define WCHAR_T_HeightBottom L"Height/Bottom" +#define WCHAR_T_Percent L"percent" +#define WCHAR_T_Pixels L"pixels" +#define WCHAR_T_Fit_Horizontal L"Fit Horizontal" +#define WCHAR_T_Fit_Vertical L"Fit Vertical" +#define WCHAR_T_Default L"Default" +#define WCHAR_T_KGGX L"KGGX" +#define WCHAR_T_KWard L"KWard" +#define WCHAR_T_Transparent L"Transparent" +#define WCHAR_T_Unspecified L"Unspecified" +#define WCHAR_T_Color L"SolidColor" +#define WCHAR_T_Linear L"Linear" +#define WCHAR_T_Phong L"Phong" +#define WCHAR_T_NPatch L"NPatch" +#define WCHAR_T_Taper L"Taper" +#define WCHAR_T_Geometry L"Geometry" +#define WCHAR_T_Painted L"Painted" +#define WCHAR_T_Filled L"Filled" +#define WCHAR_T_Stroked L"Stroked" +#define WCHAR_T_FilledAndStroked L"Filled and Stroked" +#define WCHAR_T_Simple L"Simple" +#define WCHAR_T_Smoke L"Smoke" +#define WCHAR_T_Cloud L"Cloud" +#define WCHAR_T_Fluid L"Fluid" +#define WCHAR_T_User L"User" +#define WCHAR_T_Clip L"Clip" +#define WCHAR_T_WrapWord L"WrapWord" +#define WCHAR_T_WrapAnywhere L"WrapAnywhere" + +#define CHAR_T_Directional "Directional" +#define CHAR_T_Point "Point" +#define CHAR_T_Area "Area" +#define CHAR_T_None "None" +#define CHAR_T_Vertex "Vertex" +#define CHAR_T_Pixel "Pixel" +#define CHAR_T_Normal "Normal" +#define CHAR_T_Screen "Screen" +#define CHAR_T_Multiply "Multiply" +#define CHAR_T_Overlay "Overlay" +#define CHAR_T_ColorBurn "ColorBurn" +#define CHAR_T_ColorDodge "ColorDodge" +#define CHAR_T_Add "Add" +#define CHAR_T_Subtract "Subtract" +#define CHAR_T_UV_Mapping "UV Mapping" +#define CHAR_T_Environmental_Mapping "Environmental Mapping" +#define CHAR_T_Light_Probe "Light Probe" +#define CHAR_T_No_Tiling "No Tiling" +#define CHAR_T_Mirrored "Mirrored" +#define CHAR_T_Tiled "Tiled" +#define CHAR_T_Left "Left" +#define CHAR_T_Center "Center" +#define CHAR_T_Right "Right" +#define CHAR_T_Top "Top" +#define CHAR_T_Middle "Middle" +#define CHAR_T_Bottom "Bottom" +#define CHAR_T_ElideNone "ElideNone" +#define CHAR_T_ElideLeft "ElideLeft" +#define CHAR_T_ElideMiddle "ElideMiddle" +#define CHAR_T_ElideRight "ElideRight" +#define CHAR_T_2x "2x" +#define CHAR_T_4x "4x" +#define CHAR_T_8x "8x" +#define CHAR_T_SSAA "SSAA" +#define CHAR_T_NoRotation "NoRotation" +#define CHAR_T_Clockwise90 "90" +#define CHAR_T_Clockwise180 "180" +#define CHAR_T_Clockwise270 "270" +#define CHAR_T_Fit "Fit" +#define CHAR_T_Same_Size "Same Size" +#define CHAR_T_CENTER "Center" +#define CHAR_T_North "N" +#define CHAR_T_NorthEast "NE" +#define CHAR_T_East "E" +#define CHAR_T_SouthEast "SE" +#define CHAR_T_South "S" +#define CHAR_T_SouthWest "SW" +#define CHAR_T_West "W" +#define CHAR_T_NorthWest "NW" +#define CHAR_T_LeftWidth "Left/Width" +#define CHAR_T_LeftRight "Left/Right" +#define CHAR_T_WidthRight "Width/Right" +#define CHAR_T_TopHeight "Top/Height" +#define CHAR_T_TopBottom "Top/Bottom" +#define CHAR_T_HeightBottom "Height/Bottom" +#define CHAR_T_Percent "percent" +#define CHAR_T_Pixels "pixels" +#define CHAR_T_Fit_Horizontal "Fit Horizontal" +#define CHAR_T_Fit_Vertical "Fit Vertical" +#define CHAR_T_Default "Default" +#define CHAR_T_KGGX "KGGX" +#define CHAR_T_KWard "KWard" +#define CHAR_T_Transparent "Transparent" +#define CHAR_T_Unspecified "Unspecified" +#define CHAR_T_Color "SolidColor" +#define CHAR_T_Linear "Linear" +#define CHAR_T_Phong "Phong" +#define CHAR_T_NPatch "NPatch" +#define CHAR_T_Taper "Taper" +#define CHAR_T_Geometry "Geometry" +#define CHAR_T_Painted "Painted" +#define CHAR_T_Filled "Filled" +#define CHAR_T_Stroked "Stroked" +#define CHAR_T_FilledAndStroked "Filled and Stroked" +#define CHAR_T_Simple "Simple" +#define CHAR_T_Smoke "Smoke" +#define CHAR_T_Cloud "Cloud" +#define CHAR_T_Fluid "Fluid" +#define CHAR_T_User "User" +#define CHAR_T_Clip "Clip" +#define CHAR_T_WrapWord "WrapWord" +#define CHAR_T_WrapAnywhere "WrapAnywhere" + +#define DEFINE_NAME_MAP_ENTRY(enumval, name) \ + { \ + enumval, WCHAR_T_##name, CHAR_T_##name \ + } + SEnumNameMap g_LightTypesMap[] = { + DEFINE_NAME_MAP_ENTRY(RenderLightTypes::Directional, Directional), + DEFINE_NAME_MAP_ENTRY(RenderLightTypes::Point, Point), + DEFINE_NAME_MAP_ENTRY(RenderLightTypes::Area, Area), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_MaterialLightingMap[] = { + DEFINE_NAME_MAP_ENTRY(DefaultMaterialLighting::NoLighting, None), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialLighting::VertexLighting, Vertex), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialLighting::FragmentLighting, Pixel), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_BlendModeMap[] = { + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::Normal, Normal), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::Screen, Screen), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::Multiply, Multiply), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::Overlay, Overlay), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::ColorBurn, ColorBurn), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialBlendMode::ColorDodge, ColorDodge), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_ImageMappingModeMap[] = { + DEFINE_NAME_MAP_ENTRY(ImageMappingModes::Normal, UV_Mapping), + DEFINE_NAME_MAP_ENTRY(ImageMappingModes::Environment, Environmental_Mapping), + DEFINE_NAME_MAP_ENTRY(ImageMappingModes::LightProbe, Light_Probe), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_RenderTextureCoordOpMap[] = { + DEFINE_NAME_MAP_ENTRY(NVRenderTextureCoordOp::ClampToEdge, No_Tiling), + DEFINE_NAME_MAP_ENTRY(NVRenderTextureCoordOp::MirroredRepeat, Mirrored), + DEFINE_NAME_MAP_ENTRY(NVRenderTextureCoordOp::Repeat, Tiled), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_TextHorizontalAlignmentMap[] = { + DEFINE_NAME_MAP_ENTRY(TextHorizontalAlignment::Left, Left), + DEFINE_NAME_MAP_ENTRY(TextHorizontalAlignment::Center, Center), + DEFINE_NAME_MAP_ENTRY(TextHorizontalAlignment::Right, Right), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_TextVerticalAlignmentMap[] = { + DEFINE_NAME_MAP_ENTRY(TextVerticalAlignment::Top, Top), + DEFINE_NAME_MAP_ENTRY(TextVerticalAlignment::Middle, Middle), + DEFINE_NAME_MAP_ENTRY(TextVerticalAlignment::Bottom, Bottom), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_TextWordWrapMap[] = { + DEFINE_NAME_MAP_ENTRY(TextWordWrap::Clip, Clip), + DEFINE_NAME_MAP_ENTRY(TextWordWrap::WrapWord, WrapWord), + DEFINE_NAME_MAP_ENTRY(TextWordWrap::WrapAnywhere, WrapAnywhere), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_TextElideMap[] = { + DEFINE_NAME_MAP_ENTRY(TextElide::ElideNone, ElideNone), + DEFINE_NAME_MAP_ENTRY(TextElide::ElideLeft, ElideLeft), + DEFINE_NAME_MAP_ENTRY(TextElide::ElideMiddle, ElideMiddle), + DEFINE_NAME_MAP_ENTRY(TextElide::ElideRight, ElideRight), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_ProgressiveAAValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(AAModeValues::NoAA, None), + DEFINE_NAME_MAP_ENTRY(AAModeValues::SSAA, SSAA), + DEFINE_NAME_MAP_ENTRY(AAModeValues::X2, 2x), + DEFINE_NAME_MAP_ENTRY(AAModeValues::X4, 4x), + DEFINE_NAME_MAP_ENTRY(AAModeValues::X8, 8x), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_LayerBlendTypesMap[] = { + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Normal, Normal), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Screen, Screen), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Multiply, Multiply), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Add, Add), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Subtract, Subtract), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::Overlay, Overlay), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::ColorBurn, ColorBurn), + DEFINE_NAME_MAP_ENTRY(LayerBlendTypes::ColorDodge, ColorDodge), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_RenderRotationValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(RenderRotationValues::NoRotation, None), + DEFINE_NAME_MAP_ENTRY(RenderRotationValues::Clockwise90, Clockwise90), + DEFINE_NAME_MAP_ENTRY(RenderRotationValues::Clockwise180, Clockwise180), + DEFINE_NAME_MAP_ENTRY(RenderRotationValues::Clockwise270, Clockwise270), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_CameraScaleModesMap[] = { + DEFINE_NAME_MAP_ENTRY(CameraScaleModes::Fit, Fit), + DEFINE_NAME_MAP_ENTRY(CameraScaleModes::SameSize, Same_Size), + DEFINE_NAME_MAP_ENTRY(CameraScaleModes::FitHorizontal, Fit_Horizontal), + DEFINE_NAME_MAP_ENTRY(CameraScaleModes::FitVertical, Fit_Vertical), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_CameraScaleAnchorsMap[] = { + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::Center, Center), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::North, North), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::NorthEast, NorthEast), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::East, East), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::SouthEast, SouthEast), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::South, South), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::SouthWest, SouthWest), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::West, West), + DEFINE_NAME_MAP_ENTRY(CameraScaleAnchors::NorthWest, NorthWest), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_HorizontalFieldValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(HorizontalFieldValues::LeftWidth, LeftWidth), + DEFINE_NAME_MAP_ENTRY(HorizontalFieldValues::LeftRight, LeftRight), + DEFINE_NAME_MAP_ENTRY(HorizontalFieldValues::WidthRight, WidthRight), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_VerticalFieldValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(VerticalFieldValues::TopHeight, TopHeight), + DEFINE_NAME_MAP_ENTRY(VerticalFieldValues::TopBottom, TopBottom), + DEFINE_NAME_MAP_ENTRY(VerticalFieldValues::HeightBottom, HeightBottom), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_LayerUnitTypesMap[] = { + DEFINE_NAME_MAP_ENTRY(LayerUnitTypes::Percent, Percent), + DEFINE_NAME_MAP_ENTRY(LayerUnitTypes::Pixels, Pixels), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_LayerBackgroundMap[] = { + DEFINE_NAME_MAP_ENTRY(LayerBackground::Transparent, Transparent), + DEFINE_NAME_MAP_ENTRY(LayerBackground::Unspecified, Unspecified), + DEFINE_NAME_MAP_ENTRY(LayerBackground::Color, Color), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_SpecularTypesMap[] = { + DEFINE_NAME_MAP_ENTRY(DefaultMaterialSpecularModel::Default, Default), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialSpecularModel::KGGX, KGGX), + DEFINE_NAME_MAP_ENTRY(DefaultMaterialSpecularModel::KWard, KWard), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_TessellationValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(TessModeValues::NoTess, None), + DEFINE_NAME_MAP_ENTRY(TessModeValues::TessLinear, Linear), + DEFINE_NAME_MAP_ENTRY(TessModeValues::TessPhong, Phong), + DEFINE_NAME_MAP_ENTRY(TessModeValues::TessNPatch, NPatch), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_PathCappingValuesMap[] = { + DEFINE_NAME_MAP_ENTRY(PathCapping::Noner, None), + DEFINE_NAME_MAP_ENTRY(PathCapping::Taper, Taper), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_PathTypesMap[] = { + DEFINE_NAME_MAP_ENTRY(PathTypes::Noner, None), + DEFINE_NAME_MAP_ENTRY(PathTypes::Painted, Painted), + DEFINE_NAME_MAP_ENTRY(PathTypes::Geometry, Geometry), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap g_PathPaintStylesMap[] = { + DEFINE_NAME_MAP_ENTRY(PathPaintStyles::Noner, None), + DEFINE_NAME_MAP_ENTRY(PathPaintStyles::FilledAndStroked, FilledAndStroked), + DEFINE_NAME_MAP_ENTRY(PathPaintStyles::Filled, Filled), + DEFINE_NAME_MAP_ENTRY(PathPaintStyles::Stroked, Stroked), + { (QT3DSU32)-1, NULL }, + }; + + SEnumNameMap *SEnumParseMap<RenderLightTypes::Enum>::GetMap() { return g_LightTypesMap; } + + SEnumNameMap *SEnumParseMap<DefaultMaterialLighting::Enum>::GetMap() + { + return g_MaterialLightingMap; + } + + SEnumNameMap *SEnumParseMap<DefaultMaterialBlendMode::Enum>::GetMap() { return g_BlendModeMap; } + + SEnumNameMap *SEnumParseMap<ImageMappingModes::Enum>::GetMap() { return g_ImageMappingModeMap; } + + SEnumNameMap *SEnumParseMap<NVRenderTextureCoordOp::Enum>::GetMap() + { + return g_RenderTextureCoordOpMap; + } + + SEnumNameMap *SEnumParseMap<TextHorizontalAlignment::Enum>::GetMap() + { + return g_TextHorizontalAlignmentMap; + } + + SEnumNameMap *SEnumParseMap<TextVerticalAlignment::Enum>::GetMap() + { + return g_TextVerticalAlignmentMap; + } + + SEnumNameMap *SEnumParseMap<TextWordWrap::Enum>::GetMap() + { + return g_TextWordWrapMap; + } + + SEnumNameMap *SEnumParseMap<TextElide::Enum>::GetMap() + { + return g_TextElideMap; + } + + SEnumNameMap *SEnumParseMap<AAModeValues::Enum>::GetMap() { return g_ProgressiveAAValuesMap; } + + SEnumNameMap *SEnumParseMap<LayerBlendTypes::Enum>::GetMap() { return g_LayerBlendTypesMap; } + + SEnumNameMap *SEnumParseMap<RenderRotationValues::Enum>::GetMap() + { + return g_RenderRotationValuesMap; + } + + SEnumNameMap *SEnumParseMap<CameraScaleModes::Enum>::GetMap() { return g_CameraScaleModesMap; } + + SEnumNameMap *SEnumParseMap<CameraScaleAnchors::Enum>::GetMap() + { + return g_CameraScaleAnchorsMap; + } + + SEnumNameMap *SEnumParseMap<HorizontalFieldValues::Enum>::GetMap() + { + return g_HorizontalFieldValuesMap; + } + + SEnumNameMap *SEnumParseMap<VerticalFieldValues::Enum>::GetMap() + { + return g_VerticalFieldValuesMap; + } + + SEnumNameMap *SEnumParseMap<LayerUnitTypes::Enum>::GetMap() { return g_LayerUnitTypesMap; } + + SEnumNameMap *SEnumParseMap<LayerBackground::Enum>::GetMap() { return g_LayerBackgroundMap; } + + SEnumNameMap *SEnumParseMap<DefaultMaterialSpecularModel::Enum>::GetMap() + { + return g_SpecularTypesMap; + } + + SEnumNameMap *SEnumParseMap<TessModeValues::Enum>::GetMap() { return g_TessellationValuesMap; } + + SEnumNameMap *SEnumParseMap<PathCapping::Enum>::GetMap() { return g_PathCappingValuesMap; } + + SEnumNameMap *SEnumParseMap<PathTypes::Enum>::GetMap() { return g_PathTypesMap; } + + SEnumNameMap *SEnumParseMap<PathPaintStyles::Enum>::GetMap() { return g_PathPaintStylesMap; } +} +} diff --git a/src/runtimerender/Qt3DSRenderUIPSharedTranslation.h b/src/runtimerender/Qt3DSRenderUIPSharedTranslation.h new file mode 100644 index 0000000..16ce139 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderUIPSharedTranslation.h @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_UIP_SHARED_TRANSLATION_H +#define QT3DS_RENDER_UIP_SHARED_TRANSLATION_H +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSDMWindowsCompatibility.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderModel.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPresentation.h" + +// map from qt3dsdm to qt3ds::render +namespace qt3ds { +namespace render { + + template <typename TEnumType> + struct SEnumParseMap + { + }; + + struct SEnumNameMap + { + QT3DSU32 m_Enum; + const wchar_t *m_WideName; + const char8_t *m_Name; + }; + + template <> + struct SEnumParseMap<RenderLightTypes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<DefaultMaterialLighting::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<DefaultMaterialBlendMode::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<ImageMappingModes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<NVRenderTextureCoordOp::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<TextHorizontalAlignment::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<TextVerticalAlignment::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<TextWordWrap::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<TextElide::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<AAModeValues::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<LayerBlendTypes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<RenderRotationValues::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<CameraScaleModes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<CameraScaleAnchors::Enum> + { + static SEnumNameMap *GetMap(); + }; + template <> + struct SEnumParseMap<HorizontalFieldValues::Enum> + { + static SEnumNameMap *GetMap(); + }; + template <> + struct SEnumParseMap<VerticalFieldValues::Enum> + { + static SEnumNameMap *GetMap(); + }; + template <> + struct SEnumParseMap<LayerUnitTypes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<LayerBackground::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<DefaultMaterialSpecularModel::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<TessModeValues::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<PathCapping::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<PathTypes::Enum> + { + static SEnumNameMap *GetMap(); + }; + + template <> + struct SEnumParseMap<PathPaintStyles::Enum> + { + static SEnumNameMap *GetMap(); + }; + +#define QT3DS_RENDER_WCHAR_T_XYZs L"XYZ" +#define QT3DS_RENDER_WCHAR_T_YZXs L"YZX" +#define QT3DS_RENDER_WCHAR_T_ZXYs L"ZXY" +#define QT3DS_RENDER_WCHAR_T_XZYs L"XZY" +#define QT3DS_RENDER_WCHAR_T_YXZs L"YXZ" +#define QT3DS_RENDER_WCHAR_T_ZYXs L"ZYX" + +#define QT3DS_RENDER_WCHAR_T_XYZr L"XYZr" +#define QT3DS_RENDER_WCHAR_T_YZXr L"YZXr" +#define QT3DS_RENDER_WCHAR_T_ZXYr L"ZXYr" +#define QT3DS_RENDER_WCHAR_T_XZYr L"XZYr" +#define QT3DS_RENDER_WCHAR_T_YXZr L"YXZr" +#define QT3DS_RENDER_WCHAR_T_ZYXr L"ZYXr" + +#define QT3DS_RENDER_CHAR_T_XYZs "XYZ" +#define QT3DS_RENDER_CHAR_T_YZXs "YZX" +#define QT3DS_RENDER_CHAR_T_ZXYs "ZXY" +#define QT3DS_RENDER_CHAR_T_XZYs "XZY" +#define QT3DS_RENDER_CHAR_T_YXZs "YXZ" +#define QT3DS_RENDER_CHAR_T_ZYXs "ZYX" + +#define QT3DS_RENDER_CHAR_T_XYZr "XYZr" +#define QT3DS_RENDER_CHAR_T_YZXr "YZXr" +#define QT3DS_RENDER_CHAR_T_ZXYr "ZXYr" +#define QT3DS_RENDER_CHAR_T_XZYr "XZYr" +#define QT3DS_RENDER_CHAR_T_YXZr "YXZr" +#define QT3DS_RENDER_CHAR_T_ZYXr "ZYXr" + + inline QT3DSU32 MapRotationOrder(const wchar_t *inOrderStr) + { +#define MAP_ROTATION_ORDER(name, postfix) \ + if (wcscmp(inOrderStr, QT3DS_RENDER_WCHAR_T_##name##postfix) == 0) { \ + return EulOrd##name##postfix; \ + } + MAP_ROTATION_ORDER(XYZ, s); + MAP_ROTATION_ORDER(YZX, s); + MAP_ROTATION_ORDER(ZXY, s); + MAP_ROTATION_ORDER(XZY, s); + MAP_ROTATION_ORDER(YXZ, s); + MAP_ROTATION_ORDER(ZYX, s); + MAP_ROTATION_ORDER(XYZ, r); + MAP_ROTATION_ORDER(YZX, r); + MAP_ROTATION_ORDER(ZXY, r); + MAP_ROTATION_ORDER(XZY, r); + MAP_ROTATION_ORDER(YXZ, r); + MAP_ROTATION_ORDER(ZYX, r); +#undef MAP_ROTATION_ORDER + return EulOrdYXZs; + } + + inline QT3DSU32 MapRotationOrder(const char8_t *inOrderStr) + { +#define MAP_ROTATION_ORDER(name, postfix) \ + if (strcmp(inOrderStr, QT3DS_RENDER_CHAR_T_##name##postfix) == 0) { \ + return EulOrd##name##postfix; \ + } + MAP_ROTATION_ORDER(XYZ, s); + MAP_ROTATION_ORDER(YZX, s); + MAP_ROTATION_ORDER(ZXY, s); + MAP_ROTATION_ORDER(XZY, s); + MAP_ROTATION_ORDER(YXZ, s); + MAP_ROTATION_ORDER(ZYX, s); + MAP_ROTATION_ORDER(XYZ, r); + MAP_ROTATION_ORDER(YZX, r); + MAP_ROTATION_ORDER(ZXY, r); + MAP_ROTATION_ORDER(XZY, r); + MAP_ROTATION_ORDER(YXZ, r); + MAP_ROTATION_ORDER(ZYX, r); +#undef MAP_ROTATION_ORDER + return EulOrdYXZs; + } + + // the goal is to unify the systems that transfer information into the UICRender library. + // There are currently three such systems; the runtime, studio, and the uip loader that loads + // uip files + // directly into the render library. + // To do this, we need to have a mapping between a generic key and a given property on every + // object + // along with some information about what portion of the object model this property affects. + + struct Qt3DSRenderDirtyFlags + { + enum Enum { + Unknown = 0, + Dirty = 1 << 0, + TransformDirty = 1 << 1, + TextDirty = 1 << 2, + }; + }; + +// Now we build out generic macros with no implementation that list all of the properties +// on each struct that we care about. We will fill in these macros with implementation later. +// Each macro will list the property name along with what dirty operation should get marked +// Global parse tables that list every property used by the system. + +#define ITERATE_QT3DS_RENDER_SCENE_PROPERTIES \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Scene, ClearColor, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Scene, UseClearColor, Dirty) + +#define ITERATE_QT3DS_RENDER_NODE_PROPERTIES \ + HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY(Node, Rotation, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC3_RADIAN_PROPERTY(Node, Rotation, TransformDirty) \ + HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY(Node, Position, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC3_PROPERTY(Node, Position, TransformDirty) \ + HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY(Node, Scale, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC3_PROPERTY(Node, Scale, TransformDirty) \ + HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY(Node, Pivot, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC3_PROPERTY(Node, Pivot, TransformDirty) \ + HANDLE_QT3DS_RENDER_OPACITY_PROPERTY(Node, LocalOpacity, TransformDirty) \ + HANDLE_QT3DS_ROTATION_ORDER_PROPERTY(Node, RotationOrder, TransformDirty) \ + HANDLE_QT3DS_NODE_ORIENTATION_PROPERTY(Node, LeftHanded, TransformDirty) + +#define ITERATE_QT3DS_RENDER_LAYER_PROPERTIES \ + HANDLE_QT3DS_NODE_FLAGS_INVERSE_PROPERTY(Layer, LayerEnableDepthTest, Dirty) \ + HANDLE_QT3DS_NODE_FLAGS_INVERSE_PROPERTY(Layer, LayerEnableDepthPrePass, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, ProgressiveAAMode, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, MultisampleAAMode, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, TemporalAAEnabled, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Layer, ClearColor, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, BlendType, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, Background, Dirty) \ + HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(Layer, TexturePath, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, HorizontalFieldValues, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Left, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, LeftUnits, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Width, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, WidthUnits, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Right, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, RightUnits, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, VerticalFieldValues, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Top, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, TopUnits, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Height, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, HeightUnits, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Bottom, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Layer, BottomUnits, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, AoStrength, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, AoDistance, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, AoSoftness, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, AoBias, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, AoDither, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ShadowStrength, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ShadowDist, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ShadowSoftness, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ShadowBias, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, LightProbe, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ProbeBright, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, FastIbl, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ProbeHorizon, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, ProbeFov, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, LightProbe2, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Probe2Fade, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Probe2Window, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Layer, Probe2Pos, Dirty) + +#define ITERATE_QT3DS_RENDER_CAMERA_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(Camera, ClipNear, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Camera, ClipFar, Dirty) \ + HANDLE_QT3DS_RENDER_RADIAN_PROPERTY(Camera, FOV, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Camera, FOVHorizontal, Dirty) \ + HANDLE_QT3DS_NODE_FLAGS_PROPERTY(Camera, Orthographic, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Camera, ScaleMode, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Camera, ScaleAnchor, Dirty) + +#define ITERATE_QT3DS_RENDER_LIGHT_PROPERTIES \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Light, LightType, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, Scope, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Light, DiffuseColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Light, DiffuseColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Light, SpecularColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Light, SpecularColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Light, AmbientColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Light, AmbientColor, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, Brightness, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, LinearFade, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ExponentialFade, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, AreaWidth, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, AreaHeight, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, CastShadow, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ShadowBias, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ShadowFactor, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ShadowMapFar, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ShadowMapFov, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Light, ShadowFilter, Dirty) + +#define ITERATE_QT3DS_RENDER_MODEL_PROPERTIES \ + HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(Model, MeshPath, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Model, ShadowCaster, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Model, TessellationMode, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Model, EdgeTess, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Model, InnerTess, Dirty) + +#define ITERATE_QT3DS_RENDER_CUSTOM_MATERIAL_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(MaterialBase, IblProbe, Dirty) + +#define ITERATE_QT3DS_RENDER_LIGHTMAP_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(Lightmaps, LightmapIndirect, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Lightmaps, LightmapRadiosity, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Lightmaps, LightmapShadow, Dirty) + +#define ITERATE_QT3DS_RENDER_MATERIAL_PROPERTIES \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Material, Lighting, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Material, BlendMode, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, VertexColors, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(MaterialBase, IblProbe, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Material, DiffuseColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Material, DiffuseColor, Dirty) \ + HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(Material, DiffuseMaps, 0, Dirty) \ + HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(Material, DiffuseMaps, 1, Dirty) \ + HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(Material, DiffuseMaps, 2, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, EmissivePower, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Material, EmissiveColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Material, EmissiveColor, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, EmissiveMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, EmissiveMap2, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, SpecularReflection, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, SpecularMap, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Material, SpecularModel, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Material, SpecularTint, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Material, SpecularTint, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, FresnelPower, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, IOR, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, SpecularAmount, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, SpecularRoughness, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, RoughnessMap, Dirty) \ + HANDLE_QT3DS_RENDER_OPACITY_PROPERTY(Material, Opacity, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, OpacityMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, BumpMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, BumpAmount, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, NormalMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, DisplacementMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, DisplaceAmount, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, TranslucencyMap, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, TranslucentFalloff, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, DiffuseLightWrap, Dirty) + +#define ITERATE_QT3DS_RENDER_REFERENCED_MATERIAL_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(Material, ReferencedMaterial, Dirty) + +#define ITERATE_QT3DS_RENDER_IMAGE_PROPERTIES \ + HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(Image, ImagePath, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Image, OffscreenRendererId, Dirty) \ + HANDLE_QT3DS_RENDER_VEC2_PROPERTY(Image, Scale, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC2_PROPERTY(Image, Pivot, TransformDirty) \ + HANDLE_QT3DS_RENDER_RADIAN_PROPERTY(Image, Rotation, TransformDirty) \ + HANDLE_QT3DS_RENDER_VEC2_PROPERTY(Image, Position, TransformDirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Image, MappingMode, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Image, HorizontalTilingMode, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Image, VerticalTilingMode, Dirty) + +#define ITERATE_QT3DS_RENDER_TEXT_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, Text, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, Font, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, FontSize, TextDirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Text, HorizontalAlignment, TextDirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Text, VerticalAlignment, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, Leading, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, Tracking, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, DropShadow, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, DropShadowStrength, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, DropShadowOffsetX, TextDirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, DropShadowOffsetY, TextDirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Text, WordWrap, TextDirty) \ + HANDLE_QT3DS_RENDER_REAL_VEC2_PROPERTY(Text, BoundingBox, TextDirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Text, Elide, TextDirty) \ + HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY(Text, TextColor, Dirty) \ + HANDLE_QT3DS_RENDER_COLOR_PROPERTY(Text, TextColor, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Text, EnableAcceleratedFont, Dirty) + +#define ITERATE_QT3DS_RENDER_PATH_PROPERTIES \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Path, PathType, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, Width, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, LinearError, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, EdgeTessAmount, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, InnerTessAmount, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Path, BeginCapping, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, BeginCapOffset, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, BeginCapOpacity, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, BeginCapWidth, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Path, EndCapping, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, EndCapOffset, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, EndCapOpacity, Dirty) \ + HANDLE_QT3DS_RENDER_PROPERTY(Path, EndCapWidth, Dirty) \ + HANDLE_QT3DS_RENDER_ENUM_PROPERTY(Path, PaintStyle, Dirty) \ + HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(Path, PathBuffer, Dirty) + +#define ITERATE_QT3DS_RENDER_PATH_SUBPATH_PROPERTIES \ + HANDLE_QT3DS_RENDER_PROPERTY(SubPath, Closed, Dirty) +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderWidgets.cpp b/src/runtimerender/Qt3DSRenderWidgets.cpp new file mode 100644 index 0000000..311b218 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderWidgets.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderWidgets.h" +#include "Qt3DSRenderNode.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "render/Qt3DSRenderShaderProgram.h" + +using namespace qt3ds::render; + +namespace { + +struct SWidgetBBox : public IRenderWidget +{ + NVBounds3 m_Bounds; + QT3DSVec3 m_Color; + NVRenderVertexBuffer *m_BoxVertexBuffer; + NVRenderIndexBuffer *m_BoxIndexBuffer; + NVRenderInputAssembler *m_BoxInputAssembler; + NVRenderShaderProgram *m_BoxShader; + CRegisteredString m_ItemName; + SWidgetBBox(SNode &inNode, const NVBounds3 &inBounds, const QT3DSVec3 &inColor) + : IRenderWidget(inNode) + , m_Bounds(inBounds) + , m_Color(inColor) + , m_BoxVertexBuffer(NULL) + , m_BoxIndexBuffer(NULL) + , m_BoxInputAssembler(NULL) + , m_BoxShader(NULL) + { + } + + void SetupBoxShader(IRenderWidgetContext &inContext) + { + m_BoxShader = inContext.GetShader(m_ItemName); + if (!m_BoxShader) { + qt3ds::render::IShaderProgramGenerator &theGenerator(inContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + theVertexGenerator.Append("}"); + theFragmentGenerator.AddUniform("output_color", "vec3"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tgl_FragColor.rgb = output_color;"); + theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + theFragmentGenerator.Append("}"); + m_BoxShader = inContext.CompileAndStoreShader(m_ItemName); + } + } + + void SetupBoundingBoxGraphicsObjects(IRenderWidgetContext &inContext, + NVDataRef<QT3DSVec3> thePoints) + { + qt3ds::render::NVRenderVertexBufferEntry theEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3); + m_BoxVertexBuffer = &inContext.GetOrCreateVertexBuffer( + m_ItemName, 3 * sizeof(QT3DSF32), toU8DataRef(thePoints.begin(), thePoints.size())); + m_BoxIndexBuffer = inContext.GetIndexBuffer(m_ItemName); + if (!m_BoxIndexBuffer) { + // The way the bounds lays out the bounds for the box + // capitalization indicates whether this was a max or min value. + enum _Indexes { + xyz = 0, + Xyz, + xYz, + xyZ, + XYZ, + xYZ, + XyZ, + XYz, + }; + QT3DSU8 indexes[] = { + // The toBoxBounds function lays out points such that + // xyz, Xyz, xYz, xyZ, XYZ, xYZ, XyZ, XYz + // Min corner + xyz, Xyz, xyz, xYz, xyz, xyZ, + + // Max corner + XYZ, xYZ, XYZ, XyZ, XYZ, XYz, + + // Now connect the rest of the dots. + // the rules are that only one letter can change + // else you are connecting *across* the box somehow. + + Xyz, XYz, Xyz, XyZ, + + xYz, XYz, xYz, xYZ, + + xyZ, XyZ, xyZ, xYZ, + }; + m_BoxIndexBuffer = &inContext.GetOrCreateIndexBuffer( + m_ItemName, qt3ds::render::NVRenderComponentTypes::QT3DSU8, sizeof(indexes), + toU8DataRef(indexes, sizeof(indexes))); + } + + m_BoxInputAssembler = inContext.GetInputAssembler(m_ItemName); + if (!m_BoxInputAssembler && m_BoxIndexBuffer && m_BoxVertexBuffer) { + // create our attribute layout + NVRenderAttribLayout *theAttribLAyout = + &inContext.CreateAttributeLayout(toConstDataRef(&theEntry, 1)); + + QT3DSU32 strides = m_BoxVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_BoxInputAssembler = &inContext.GetOrCreateInputAssembler( + m_ItemName, theAttribLAyout, toConstDataRef(&m_BoxVertexBuffer, 1), + m_BoxIndexBuffer, toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + SetupBoxShader(inContext); + } + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + m_ItemName = inRenderContext.GetStringTable().RegisterStr("SWidgetBBox"); + SWidgetRenderInformation theInfo(inWidgetContext.GetWidgetRenderInformation( + *m_Node, m_Node->m_Position, RenderWidgetModes::Local)); + TNVBounds2BoxPoints thePoints; + m_Bounds.expand(thePoints); + QT3DSMat44 theNodeRotation; + QT3DSMat44 theNodeToCamera = theInfo.m_NodeParentToCamera * m_Node->m_LocalTransform; + for (QT3DSU32 idx = 0; idx < 8; ++idx) + thePoints[idx] = theNodeToCamera.transform(thePoints[idx]); + SetupBoundingBoxGraphicsObjects(inWidgetContext, toDataRef(thePoints, 8)); + if (m_BoxShader && m_BoxInputAssembler) { + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_BoxShader); + m_BoxShader->SetPropertyValue("model_view_projection", theInfo.m_LayerProjection); + m_BoxShader->SetPropertyValue("output_color", m_Color); + inRenderContext.SetInputAssembler(m_BoxInputAssembler); + inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Lines, + m_BoxInputAssembler->GetIndexCount(), 0); + } + } +}; + +struct SWidgetAxis : public IRenderWidget +{ + NVRenderVertexBuffer *m_AxisVertexBuffer; + NVRenderInputAssembler *m_AxisInputAssembler; + NVRenderShaderProgram *m_AxisShader; + CRegisteredString m_ItemName; + + SWidgetAxis(SNode &inNode) + : IRenderWidget(inNode) + , m_AxisVertexBuffer(NULL) + , m_AxisInputAssembler(NULL) + , m_AxisShader(NULL) + { + } + + void SetupAxisShader(IRenderWidgetContext &inContext) + { + m_AxisShader = inContext.GetShader(m_ItemName); + if (!m_AxisShader) { + qt3ds::render::IShaderProgramGenerator &theGenerator(inContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_color", "vec3"); + theVertexGenerator.AddOutgoing("output_color", "vec3"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + theVertexGenerator.Append("\toutput_color = attr_color;"); + theVertexGenerator.Append("}"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tgl_FragColor.rgb = output_color;"); + theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + theFragmentGenerator.Append("}"); + m_AxisShader = inContext.CompileAndStoreShader(m_ItemName); + } + } + + void SetupAxesGraphicsObjects(IRenderWidgetContext &inContext, NVDataRef<QT3DSVec3> theAxes) + { + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_color", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 12), + }; + + m_AxisVertexBuffer = &inContext.GetOrCreateVertexBuffer( + m_ItemName, 6 * sizeof(QT3DSF32), toU8DataRef(theAxes.begin(), theAxes.size())); + m_AxisInputAssembler = inContext.GetInputAssembler(m_ItemName); + if (!m_AxisInputAssembler && m_AxisVertexBuffer) { + // create our attribute layout + NVRenderAttribLayout *theAttribLAyout = + &inContext.CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + QT3DSU32 strides = m_AxisVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_AxisInputAssembler = &inContext.GetOrCreateInputAssembler( + m_ItemName, theAttribLAyout, toConstDataRef(&m_AxisVertexBuffer, 1), nullptr, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + } + + inline QT3DSVec3 TransformDirection(const QT3DSMat33 &inMatrix, const QT3DSVec3 &inDir) + { + QT3DSVec3 retval = inMatrix.transform(inDir); + return retval; + } + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + m_ItemName = inRenderContext.GetStringTable().RegisterStr("SWidgetAxis"); + + SetupAxisShader(inWidgetContext); + + if (m_AxisShader) { + static const QT3DSVec3 pivotCol = QT3DSVec3(0, 0, 1); + if (m_Node->m_Parent && m_Node->m_Parent->m_Type != GraphObjectTypes::Layer) { + m_Node->m_Parent->CalculateGlobalVariables(); + } + QT3DSVec3 thePivot(m_Node->m_Pivot); + if (m_Node->m_Flags.IsLeftHanded()) + thePivot.z *= -1; + + SWidgetRenderInformation theInfo(inWidgetContext.GetWidgetRenderInformation( + *m_Node, QT3DSVec3(0, 0, 0), RenderWidgetModes::Local)); + + QT3DSMat44 theNodeRotation; + m_Node->CalculateRotationMatrix(theNodeRotation); + if (m_Node->m_Flags.IsLeftHanded()) + SNode::FlipCoordinateSystem(theNodeRotation); + + QT3DSMat33 theRotationMatrix(theNodeRotation.column0.getXYZ(), + theNodeRotation.column1.getXYZ(), + theNodeRotation.column2.getXYZ()); + + // Move the camera position into camera space. This is so that when we render we don't + // have to account + // for scaling done in the camera's MVP. + QT3DSVec3 theItemPosition = theInfo.m_Position; + + QT3DSMat33 theAxisTransform = theInfo.m_NormalMatrix * theRotationMatrix; + + // Scale the effective pivot line end point according to node scale + // so that pivot line always hits object center. + thePivot = thePivot.multiply(m_Node->m_Scale); + QT3DSVec3 pivotVec = TransformDirection( + theAxisTransform, QT3DSVec3(-thePivot.x, -thePivot.y, -thePivot.z)); + + QT3DSVec3 thePivotLine[] = { + theItemPosition, pivotCol, theItemPosition + pivotVec, pivotCol + }; + + SetupAxesGraphicsObjects(inWidgetContext, toDataRef(thePivotLine, 4)); + + if (m_AxisInputAssembler) { + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_AxisShader); + m_AxisShader->SetPropertyValue("model_view_projection", theInfo.m_LayerProjection); + inRenderContext.SetInputAssembler(m_AxisInputAssembler); + // Draw line from pivot to object center. + inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Lines, 2, 0); + } + } + } +}; +} + +IRenderWidget &IRenderWidget::CreateBoundingBoxWidget(SNode &inNode, const NVBounds3 &inBounds, + const QT3DSVec3 &inColor, + NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SWidgetBBox)(inNode, inBounds, inColor); +} + +IRenderWidget &IRenderWidget::CreateAxisWidget(SNode &inNode, NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SWidgetAxis)(inNode); +} diff --git a/src/runtimerender/Qt3DSRenderWidgets.h b/src/runtimerender/Qt3DSRenderWidgets.h new file mode 100644 index 0000000..28cb332 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderWidgets.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_WIDGETS_H +#define QT3DS_RENDER_WIDGETS_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSOption.h" +#include "foundation/Qt3DSMat44.h" +#include "foundation/Qt3DSMat33.h" +#include "foundation/Qt3DSBounds3.h" +#include "foundation/Qt3DSVec3.h" +#include "EASTL/utility.h" +#include "foundation/Qt3DSDataRef.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "render/Qt3DSRenderIndexBuffer.h" +#include "Qt3DSRenderText.h" + +namespace qt3ds { +namespace render { + + struct SWidgetRenderInformation + { + // Just the rotation component of the nodeparenttocamera. + QT3DSMat33 m_NormalMatrix; + // The node parent's global transform multiplied by the inverse camera global transfrom; + // basically the MV from model-view-projection + QT3DSMat44 m_NodeParentToCamera; + // Projection that accounts for layer scaling + QT3DSMat44 m_LayerProjection; + // Pure camera projection without layer scaling + QT3DSMat44 m_PureProjection; + // A look at matrix that will rotate objects facing directly up + // the Z axis such that the point to the camera. + QT3DSMat33 m_LookAtMatrix; + // Conversion from world to camera position so world points not in object + // local space can be converted to camera space without going through the node's + // inverse global transform + QT3DSMat44 m_CameraGlobalInverse; + // Offset to add to the node's world position in camera space to move to the ideal camera + // location so that scale will work. This offset should be added *after* translation into + // camera space + QT3DSVec3 m_WorldPosOffset; + // Position in camera space to center the widget around + QT3DSVec3 m_Position; + // Scale factor to scale the widget by. + QT3DSF32 m_Scale; + + // The camera used to render this object. + SCamera *m_Camera; + SWidgetRenderInformation(const QT3DSMat33 &inNormal, const QT3DSMat44 &inNodeParentToCamera, + const QT3DSMat44 &inLayerProjection, const QT3DSMat44 &inProjection, + const QT3DSMat33 &inLookAt, const QT3DSMat44 &inCameraGlobalInverse, + const QT3DSVec3 &inWorldPosOffset, const QT3DSVec3 &inPos, QT3DSF32 inScale, + SCamera &inCamera) + : m_NormalMatrix(inNormal) + , m_NodeParentToCamera(inNodeParentToCamera) + , m_LayerProjection(inLayerProjection) + , m_PureProjection(inProjection) + , m_LookAtMatrix(inLookAt) + , m_CameraGlobalInverse(inCameraGlobalInverse) + , m_WorldPosOffset(inWorldPosOffset) + , m_Position(inPos) + , m_Scale(inScale) + , m_Camera(&inCamera) + { + } + SWidgetRenderInformation() + : m_Camera(NULL) + { + } + }; + typedef eastl::pair<SShaderVertexCodeGenerator &, SShaderFragmentCodeGenerator &> + TShaderGeneratorPair; + + struct RenderWidgetModes + { + enum Enum { + Local, + Global, + }; + }; + // Context used to get render data for the widget. + class IRenderWidgetContext + { + protected: + virtual ~IRenderWidgetContext() {} + public: + virtual NVRenderVertexBuffer & + GetOrCreateVertexBuffer(CRegisteredString &inStr, QT3DSU32 stride, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) = 0; + virtual NVRenderIndexBuffer & + GetOrCreateIndexBuffer(CRegisteredString &inStr, + qt3ds::render::NVRenderComponentTypes::Enum componentType, size_t size, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) = 0; + virtual NVRenderAttribLayout & + CreateAttributeLayout(NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> attribs) = 0; + virtual NVRenderInputAssembler & + GetOrCreateInputAssembler(CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef<NVRenderVertexBuffer *> buffers, + const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef<QT3DSU32> strides, NVConstDataRef<QT3DSU32> offsets) = 0; + + virtual NVRenderVertexBuffer *GetVertexBuffer(CRegisteredString &inStr) = 0; + virtual NVRenderIndexBuffer *GetIndexBuffer(CRegisteredString &inStr) = 0; + virtual NVRenderInputAssembler *GetInputAssembler(CRegisteredString &inStr) = 0; + + virtual NVRenderShaderProgram *GetShader(CRegisteredString inStr) = 0; + virtual IShaderProgramGenerator &GetProgramGenerator() = 0; + // calls compile on the program generator and stores result under this name. + virtual NVRenderShaderProgram *CompileAndStoreShader(CRegisteredString inStr) = 0; + virtual STextDimensions MeasureText(const STextRenderInfo &inText) = 0; + // Render text using a specific MVP + virtual void RenderText(const STextRenderInfo &inText, const QT3DSVec3 &inTextColor, + const QT3DSVec3 &inBackgroundColor, const QT3DSMat44 &inMVP) = 0; + // Given a node and a point in the node's local space (most likely its pivot point), we + // return + // a normal matrix so you can get the axis out, a transformation from node to camera + // a new position and a floating point scale factor so you can render in 1/2 perspective + // mode + // or orthographic mode if you would like to. + virtual SWidgetRenderInformation + GetWidgetRenderInformation(SNode &inNode, const QT3DSVec3 &inPos, + RenderWidgetModes::Enum inWidgetMode) = 0; + }; + + class IRenderWidget + { + protected: + virtual ~IRenderWidget() {} + SNode *m_Node; + + public: + IRenderWidget(SNode &inNode) + : m_Node(&inNode) + { + } + IRenderWidget() + : m_Node(NULL) + { + } + virtual void Render(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) = 0; + SNode &GetNode() { return *m_Node; } + + // Pure widgets. + static IRenderWidget &CreateBoundingBoxWidget(SNode &inNode, const NVBounds3 &inBounds, + const QT3DSVec3 &inColor, + NVAllocatorCallback &inAlloc); + static IRenderWidget &CreateAxisWidget(SNode &inNode, NVAllocatorCallback &inAlloc); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRenderableImage.h b/src/runtimerender/Qt3DSRenderableImage.h new file mode 100644 index 0000000..5d4aa1c --- /dev/null +++ b/src/runtimerender/Qt3DSRenderableImage.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERABLE_IMAGE_H +#define QT3DS_RENDERABLE_IMAGE_H +#include "Qt3DSRender.h" + +namespace qt3ds { +namespace render { + + struct ImageMapTypes + { + enum Enum { + Unknown = 0, + Diffuse = 1, + Opacity = 2, + Specular = 3, + Emissive = 4, + Bump = 5, + SpecularAmountMap = 6, + Normal = 7, + Displacement = 8, + Translucency = 9, + LightmapIndirect = 10, + LightmapRadiosity = 11, + LightmapShadow = 12, + Roughness = 13, + }; + }; + + /** + * Some precomputed information on a given image. When generating a renderable, the shader + * generator goes through all the possible images on a material and for each valid image + * computes this renderable image and attaches it to the renderable. + */ + struct SRenderableImage + { + ImageMapTypes::Enum m_MapType; + SImage &m_Image; + SRenderableImage *m_NextImage; + SRenderableImage(ImageMapTypes::Enum inMapType, SImage &inImage) + : m_MapType(inMapType) + , m_Image(inImage) + , m_NextImage(NULL) + { + } + }; +} +} +#endif diff --git a/src/runtimerender/Qt3DSRenderer.h b/src/runtimerender/Qt3DSRenderer.h new file mode 100644 index 0000000..db3eee8 --- /dev/null +++ b/src/runtimerender/Qt3DSRenderer.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_H +#define QT3DS_RENDERER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSFlags.h" +#include "EASTL/algorithm.h" //pair +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSVec2.h" +#include "Qt3DSRenderGraphObjectPickQuery.h" +#include "Qt3DSRenderCamera.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "Qt3DSRenderRay.h" + +namespace qt3ds { +namespace render { + + class IRenderableObject; + struct SModel; + struct SText; + struct SCamera; + struct SLight; + struct SLayer; + class IBufferManager; + typedef void *SRenderInstanceId; + + using qt3ds::foundation::NVConstDataRef; + + class IQt3DSRenderNodeFilter + { + protected: + virtual ~IQt3DSRenderNodeFilter() {} + public: + virtual bool IncludeNode(const SNode &inNode) = 0; + }; + struct SLayerPickSetup + { + QT3DSMat44 m_ProjectionPreMultiply; + QT3DSMat44 m_ViewProjection; + NVRenderRect m_ScissorRect; + SLayerPickSetup(const QT3DSMat44 &inProjPreMult, const QT3DSMat44 &inVP, + const NVRenderRect &inScissor) + : m_ProjectionPreMultiply(inProjPreMult) + , m_ViewProjection(inVP) + , m_ScissorRect(inScissor) + { + } + SLayerPickSetup() {} + }; + + struct SScaleAndPosition + { + QT3DSVec3 m_Position; + QT3DSF32 m_Scale; + SScaleAndPosition(const QT3DSVec3 &inPos, QT3DSF32 inScale) + : m_Position(inPos) + , m_Scale(inScale) + { + } + SScaleAndPosition() {} + }; + + class IQt3DSRenderer : public NVRefCounted + { + protected: + virtual ~IQt3DSRenderer() {} + + public: + virtual void EnableLayerCaching(bool inEnabled) = 0; + virtual bool IsLayerCachingEnabled() const = 0; + virtual void EnableLayerGpuProfiling(bool inEnabled) = 0; + virtual bool IsLayerGpuProfilingEnabled() const = 0; + + // Get the camera that rendered this node last render + virtual SCamera *GetCameraForNode(const SNode &inNode) const = 0; + virtual Option<SCuboidRect> GetCameraBounds(const SGraphObject &inObject) = 0; + // Called when you have changed the number or order of children of a given node. + virtual void ChildrenUpdated(SNode &inParent) = 0; + virtual QT3DSF32 GetTextScale(const SText &inText) = 0; + + // The IQt3DSRenderContext calls these, clients should not. + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + + // Setup the vertex and index buffers (but not shader state) + // and render the quad. The quad is setup so that its edges + // go from -1,1 in x,y and its UV coordinates will map naturally + // to an image. + virtual void RenderQuad() = 0; + + // Render a given texture to the scene using a given transform. + virtual void RenderQuad(const QT3DSVec2 inDimensions, const QT3DSMat44 &inMVP, + NVRenderTexture2D &inQuadTexture) = 0; + + // This point rendering works uisng indirect array drawing + // This means you need to setup a GPU buffer + // which contains the drawing information + virtual void RenderPointsIndirect() = 0; + + // Returns true if this layer or a sibling was dirty. + virtual bool PrepareLayerForRender(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool inRenderSiblings = true, + const SRenderInstanceId id = nullptr) = 0; + virtual void RenderLayer(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, bool clear, + QT3DSVec4 clearColor, bool inRenderSiblings = true, + const SRenderInstanceId id = nullptr) = 0; + + // Studio option to disable picking against sub renderers. This allows better interaction + // in studio. + // In pick siblings measn pick the layer siblings; this is the normal behavior. + // InPickEverything means ignore the node's pick flags; this allows us to only pick things + // that have handlers + // in some cases and just pick everything in other things. + virtual void PickRenderPlugins(bool inPick) = 0; + virtual Qt3DSRenderPickResult Pick(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickSiblings = true, + bool inPickEverything = false, + const SRenderInstanceId id = nullptr) = 0; + + // Return the relative hit position, in UV space, of a mouse pick against this object. + // We need the node in order to figure out which layer rendered this object. + // We need mapper objects if this is a in a subpresentation because we have to know how + // to map the mouse coordinates into the subpresentation. So for instance if inNode is in + // a subpres then we need to know which image is displaying the subpres in order to map + // the mouse coordinates into the subpres's render space. + virtual Option<QT3DSVec2> FacePosition(SNode &inNode, NVBounds3 inBounds, + const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + NVDataRef<SGraphObject *> inMapperObjects, + SBasisPlanes::Enum inIsectPlane) = 0; + + virtual QT3DSVec3 UnprojectToPosition(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec2 &inMouseVec) const = 0; + virtual QT3DSVec3 UnprojectWithDepth(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec3 &inMouseVec) const = 0; + virtual QT3DSVec3 ProjectPosition(SNode &inNode, const QT3DSVec3 &inPosition) const = 0; + + // Roughly equivalent of gluPickMatrix, allows users to setup a perspective transform that + // will draw some sub component + // of the layer. Used in combination with an expected viewport of 0,0,width,height the + // viewproj matrix returned will center + // around the center of the viewport and render just the part of the layer around this area. + // The return value is optional because if the mouse point is completely outside the layer + // obviously this method is irrelevant. + virtual Option<SLayerPickSetup> GetLayerPickSetup(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QSize &inPickDims) = 0; + + // Return the layer's viewport rect after the layer's member variables have been applied. + // Uses the last rendered viewport rect. + virtual Option<NVRenderRectF> GetLayerRect(SLayer &inLayer) = 0; + // 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; + + // This allocator is cleared every frame on BeginFrame. Objects constructed using this + // allocator + // Must not need their destructors called. Objects are allocate on 4 byte boundaries using + // this allocator + // regardless + virtual NVAllocatorCallback &GetPerFrameAllocator() = 0; + + // Render the layer's rect onscreen. Will only render one frame, you need to call this + // every frame + // for this to work and be persistent. + virtual void RenderLayerRect(SLayer &inLayer, const QT3DSVec3 &inColor) = 0; + // Render widgets are things that are draw on the layer's widget texture which is then + // rendered to the + // scene's widget texture. You must add them every frame you wish them to be rendered; the + // list of + // widgets is cleared every frame. + virtual void AddRenderWidget(IRenderWidget &inWidget) = 0; + + // Get a scale factor so you can have objects precisely 50 pixels. Note that this scale + // factor + // only applies to things drawn parallel to the camera plane; If you aren't parallel then + // there isn't + // a single scale factor that will work. + // For perspective-rendered objects, we shift the object forward or backwards along the + // vector from the camera + // to the object so that we are working in a consistent mathematical space. So if the + // camera is orthographic, + // you are done. + // If the camera is perspective, then this method will tell you want you need to scale + // things by to account for + // the FOV and also where the origin of the object needs to be to ensure the scale factor is + // relevant. + virtual SScaleAndPosition GetWorldToPixelScaleFactor(SLayer &inLayer, + const QT3DSVec3 &inWorldPoint) = 0; + // Called before a layer goes completely out of scope to release any rendering resources + // related to the layer. + virtual void ReleaseLayerRenderResources(SLayer &inLayer, const SRenderInstanceId id) = 0; + + // render a screen aligned 2D text + virtual void RenderText2D(QT3DSF32 x, QT3DSF32 y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) = 0; + // render Gpu profiler values + virtual void RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) = 0; + + // Get the mouse coordinates as they relate to a given layer + virtual Option<QT3DSVec2> GetLayerMouseCoords(SLayer &inLayer, const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect = false) const = 0; + + virtual IRenderWidgetContext &GetRenderWidgetContext() = 0; + + static bool IsGlEsContext(qt3ds::render::NVRenderContextType inContextType); + static bool IsGlEs3Context(qt3ds::render::NVRenderContextType inContextType); + static bool IsGl2Context(qt3ds::render::NVRenderContextType inContextType); + static const char *GetGlslVesionString(qt3ds::render::NVRenderContextType inContextType); + + static IQt3DSRenderer &CreateRenderer(IQt3DSRenderContext &inContext); + }; +} +} + +#endif diff --git a/src/runtimerender/Qt3DSRendererUtil.cpp b/src/runtimerender/Qt3DSRendererUtil.cpp new file mode 100644 index 0000000..7bc108e --- /dev/null +++ b/src/runtimerender/Qt3DSRendererUtil.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRendererUtil.h" +#include "Qt3DSRenderResourceBufferObjects.h" +#include "Qt3DSRenderResourceTexture2D.h" + +using namespace qt3ds::render; + +void CRendererUtil::ResolveMutisampleFBOColorOnly(IResourceManager &inManager, + CResourceTexture2D &ioResult, + NVRenderContext &inRenderContext, QT3DSU32 inWidth, + QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inColorFormat, + NVRenderFrameBuffer &inSourceFBO) +{ + // create resolve FBO + CResourceFrameBuffer theResolveFB(inManager); + // Allocates the frame buffer which has the side effect of setting the current render target to + // that frame buffer. + theResolveFB.EnsureFrameBuffer(); + // set copy flags + qt3ds::render::NVRenderClearFlags copyFlags(NVRenderClearValues::Color); + + // get / create resolve targets and attach + ioResult.EnsureTexture(inWidth, inHeight, inColorFormat); + theResolveFB->Attach(NVRenderFrameBufferAttachments::Color0, *ioResult); + // CN - I don't believe we have to resolve the depth. + // The reason is we render the depth texture specially unresolved. So there is no need to + // resolve + // the depth prepass texture to anything else. + + // 1. Make resolve buffer be the render target ( already happend ) + // 2. Make the current layer FBO the current read target + // 3. Do the blit from MSAA to non MSAA + + // 2. + inRenderContext.SetReadTarget(&inSourceFBO); + inRenderContext.SetReadBuffer(NVReadFaces::Color0); + // 3. + inRenderContext.BlitFramebuffer(0, 0, inWidth, inHeight, 0, 0, inWidth, inHeight, copyFlags, + NVRenderTextureMagnifyingOp::Nearest); +} + +void CRendererUtil::ResolveSSAAFBOColorOnly(IResourceManager &inManager, + CResourceTexture2D &ioResult, QT3DSU32 outWidth, + QT3DSU32 outHeight, NVRenderContext &inRenderContext, + QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inColorFormat, + NVRenderFrameBuffer &inSourceFBO) +{ + // create resolve FBO + CResourceFrameBuffer theResolveFB(inManager); + // Allocates the frame buffer which has the side effect of setting the current render target to + // that frame buffer. + theResolveFB.EnsureFrameBuffer(); + // set copy flags + qt3ds::render::NVRenderClearFlags copyFlags(NVRenderClearValues::Color); + + // get / create resolve targets and attach + ioResult.EnsureTexture(outWidth, outHeight, inColorFormat); + theResolveFB->Attach(NVRenderFrameBufferAttachments::Color0, *ioResult); + // CN - I don't believe we have to resolve the depth. + // The reason is we render the depth texture specially unresolved. So there is no need to + // resolve + // the depth prepass texture to anything else. + + // 1. Make resolve buffer be the render target ( already happend ) + // 2. Make the current layer FBO the current read target + // 3. Do the blit from High res to low res buffer + + // 2. + inRenderContext.SetReadTarget(&inSourceFBO); + inRenderContext.SetReadBuffer(NVReadFaces::Color0); + // 3. + inRenderContext.BlitFramebuffer(0, 0, inWidth, inHeight, 0, 0, outWidth, outHeight, copyFlags, + NVRenderTextureMagnifyingOp::Linear); +} + +void CRendererUtil::GetSSAARenderSize(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 &outWidth, + QT3DSU32 &outHeight) +{ + // we currently double width and height + outWidth = inWidth * 2; + outHeight = inHeight * 2; + + // keep aspect ration? + // clamp to max + if (outWidth > MAX_SSAA_DIM) + outWidth = MAX_SSAA_DIM; + if (outHeight > MAX_SSAA_DIM) + outHeight = MAX_SSAA_DIM; +} diff --git a/src/runtimerender/Qt3DSRendererUtil.h b/src/runtimerender/Qt3DSRendererUtil.h new file mode 100644 index 0000000..96f3642 --- /dev/null +++ b/src/runtimerender/Qt3DSRendererUtil.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_UTIL_H +#define QT3DS_RENDERER_UTIL_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderBaseTypes.h" +namespace qt3ds { +namespace render { + class CRendererUtil + { + static const QT3DSU32 MAX_SSAA_DIM = 8192; // max render traget size for SSAA mode + + public: + static void ResolveMutisampleFBOColorOnly(IResourceManager &inManager, + CResourceTexture2D &ioResult, + NVRenderContext &inRenderContext, QT3DSU32 inWidth, + QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inColorFormat, + NVRenderFrameBuffer &inSourceFBO); + + static void ResolveSSAAFBOColorOnly(IResourceManager &inManager, + CResourceTexture2D &ioResult, QT3DSU32 outWidth, + QT3DSU32 outHeight, NVRenderContext &inRenderContext, + QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inColorFormat, + NVRenderFrameBuffer &inSourceFBO); + + static void GetSSAARenderSize(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 &outWidth, + QT3DSU32 &outHeight); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/runtimerender/Qt3DSTextRenderer.cpp b/src/runtimerender/Qt3DSTextRenderer.cpp new file mode 100644 index 0000000..91a5eb7 --- /dev/null +++ b/src/runtimerender/Qt3DSTextRenderer.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSTextRenderer.h" +#include "render/Qt3DSRenderTexture2D.h" + +using namespace qt3ds::render; + +// http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html +QT3DSU32 ITextRenderer::NextPowerOf2(QT3DSU32 input) +{ + // Algorithm doesn't work for 0 or QT3DS_MAX_U32 + QT3DS_ASSERT(input > 0 && input < QT3DS_MAX_U32); + input--; + input = (input >> 1) | input; + input = (input >> 2) | input; + input = (input >> 4) | input; + input = (input >> 8) | input; + input = (input >> 16) | input; + input++; // input is now the next highest power of 2. + return input; +} + +QT3DSU32 ITextRenderer::NextMultipleOf4(QT3DSU32 inValue) +{ + QT3DSU32 remainder(inValue % 4); + if (remainder != 0) + inValue = inValue + (4 - remainder); + + return inValue; +} + +STextTextureDetails ITextRenderer::UploadData(NVDataRef<QT3DSU8> inTextureData, + NVRenderTexture2D &inTexture, QT3DSU32 inDataWidth, + QT3DSU32 inDataHeight, QT3DSU32 inTextWidth, + QT3DSU32 inTextHeight, + NVRenderTextureFormats::Enum inFormat, + bool inFlipYAxis) +{ + if (inTextWidth == 0 || inTextHeight == 0) { + QT3DSU32 black[] = { 0, 0, 0, 0 }; + inTexture.SetTextureData(toU8DataRef(black, 4), 0, 2, 2, NVRenderTextureFormats::RGBA8); + return STextTextureDetails(2, 2, false, QT3DSVec2(1.0)); + } + QT3DS_ASSERT(NextMultipleOf4(inDataWidth) == inDataWidth); + QT3DSU32 theNecessaryHeight = NextMultipleOf4(inTextHeight); + QT3DSU32 dataStride = inDataWidth * NVRenderTextureFormats::getSizeofFormat(inFormat); + if (inTextureData.size() < dataStride * inDataHeight) { + QT3DS_ASSERT(false); + return STextTextureDetails(); + } + + STextureDetails theTextureDetails = inTexture.GetTextureDetails(); + QT3DSU32 theUploadSize = theNecessaryHeight * dataStride; + + NVDataRef<QT3DSU8> theUploadData = NVDataRef<QT3DSU8>(inTextureData.begin(), theUploadSize); + inTexture.SetTextureData(theUploadData, 0, inDataWidth, theNecessaryHeight, inFormat); + inTexture.SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Linear); + inTexture.SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Linear); + return STextTextureDetails(inTextWidth, inTextHeight, inFlipYAxis, QT3DSVec2(1.0f, 1.0f)); +} diff --git a/src/runtimerender/Qt3DSTextRenderer.h b/src/runtimerender/Qt3DSTextRenderer.h new file mode 100644 index 0000000..11c9ec6 --- /dev/null +++ b/src/runtimerender/Qt3DSTextRenderer.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_TEXT_RENDERER_H +#define QT3DS_TEXT_RENDERER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderTextTypes.h" + +namespace qt3ds { +namespace render { + + struct SRendererFontEntry + { + QString m_FontName; + QString m_FontFile; + SRendererFontEntry() {} + SRendererFontEntry(QString name, QString file) + : m_FontName(name) + , m_FontFile(file) + { + } + }; + + class ITextRendererCore : public NVRefCounted + { + public: + // You can have several standard font directories and these will be persistent + virtual void AddSystemFontDirectory(const char8_t *inDirectory) = 0; + // Should be called to clear the current context. + virtual void AddProjectFontDirectory(const char8_t *inProjectDirectory) = 0; + virtual void ClearProjectFontDirectories() = 0; + // Force font loading *right now* + virtual void PreloadFonts() = 0; + // Do not access object in between begin/end preload pairs. + virtual void BeginPreloadFonts(IThreadPool &inThreadPool, IPerfTimer &inTimer) = 0; + virtual void EndPreloadFonts() = 0; + // Force a clear and reload of all of the fonts. + virtual void ReloadFonts() = 0; + // Get the list of project fonts. These are the only fonts that can be displayed. + virtual NVConstDataRef<SRendererFontEntry> GetProjectFontList() = 0; + // The name stored in the ttf file isn't the actual name we use; we use the file stems. + // But we used to use the name. So this provides a sort of first-come-first-serve remapping + // from ttf-name to file-stem. + virtual Option<CRegisteredString> GetFontNameForFont(CRegisteredString inFontname) = 0; + virtual Option<CRegisteredString> GetFontNameForFont(const char8_t *inFontname) = 0; + + virtual ITextRenderer &GetTextRenderer(NVRenderContext &inContext) = 0; + + 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); + }; + /** + * Opaque text rendering system. Must be able to render text to an opengl texture object. + */ + class ITextRenderer : public ITextRendererCore + { + protected: + virtual ~ITextRenderer() {} + + public: + // Measure text will inText if it isn't null or the text on the info if inText is null + virtual STextDimensions MeasureText(const STextRenderInfo &inText, QT3DSF32 inTextScaleFactor, + const char8_t *inTextOverride = NULL) = 0; + // The system will use the 'r' channel as an alpha mask in order to render the + // text. You can assume GetTextDimensions was called *just* prior to this. + // It is a good idea to ensure the texture is a power of two as not all rendering systems + // support nonpot textures. Our text rendering algorithms will render a sub-rect of the + // image + // assuming it is located toward the upper-left of the image and we are also capable of + // flipping + // the image. + virtual STextTextureDetails RenderText(const STextRenderInfo &inText, + NVRenderTexture2D &inTexture) = 0; + // this is for rendering text with NV path rendering + virtual STextTextureDetails + RenderText(const STextRenderInfo &inText, NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inPathFontSpecicification) = 0; + // this is for rednering text using a texture atlas + virtual SRenderTextureAtlasDetails RenderText(const STextRenderInfo &inText) = 0; + + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + + // these two function are for texture atlas usage only + // returns the atlas entries count + virtual QT3DSI32 CreateTextureAtlas() = 0; + virtual STextTextureAtlasEntryDetails RenderAtlasEntry(QT3DSU32 index, + NVRenderTexture2D &inTexture) = 0; + + // Helper function to upload the texture data to the texture + // Will resize texture as necessary and upload using texSubImage for + // quickest upload times + // This function expects that the dataWidth to be divisible by four and + // that the total data height is larger then inTextHeight *and* divisible by four. + // and that textWidth and textHeight are less than or equal to dataWidth,dataHeight + //,can be zero, and don't need to be divisible by four (or 2). + static STextTextureDetails + UploadData(NVDataRef<QT3DSU8> inTextureData, NVRenderTexture2D &inTexture, QT3DSU32 inDataWidth, + QT3DSU32 inDataHeight, QT3DSU32 inTextWidth, QT3DSU32 inTextHeight, + NVRenderTextureFormats::Enum inFormat, bool inFlipYAxis); + + // Helper function to return the next power of two. + // Fails for values of 0 or QT3DS_MAX_U32 + static QT3DSU32 NextPowerOf2(QT3DSU32 inValue); + // If inValue is divisible by four, then return inValue + // else next largest number that is divisible by four. + static QT3DSU32 NextMultipleOf4(QT3DSU32 inValue); + }; +} +} + +#endif diff --git a/src/runtimerender/android/DynamicLibLoader.h b/src/runtimerender/android/DynamicLibLoader.h new file mode 100644 index 0000000..c968419 --- /dev/null +++ b/src/runtimerender/android/DynamicLibLoader.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "linux/DynamicLibLoader.h" diff --git a/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp b/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp new file mode 100644 index 0000000..b9f9c20 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderCamera.h" +#include "Qt3DSRenderPresentation.h" +#include "foundation/Qt3DSVec2.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSTextRenderer.h" + +#include <qmath.h> + +using namespace qt3ds::render; + +namespace { + +QT3DSF32 GetAspectRatio(const NVRenderRectF &inViewport) +{ + return inViewport.m_Height != 0 ? inViewport.m_Width / inViewport.m_Height : 0.0f; +} + +QT3DSF32 GetAspectRatio(const QT3DSVec2 &inDimensions) +{ + return inDimensions.y != 0 ? inDimensions.x / inDimensions.y : 0.0f; +} + +bool IsCameraVerticalAdjust(CameraScaleModes::Enum inMode, QT3DSF32 inDesignAspect, + QT3DSF32 inActualAspect) +{ + return (inMode == CameraScaleModes::Fit && inActualAspect >= inDesignAspect) + || inMode == CameraScaleModes::FitVertical; +} + +bool IsCameraHorizontalAdjust(CameraScaleModes::Enum inMode, QT3DSF32 inDesignAspect, + QT3DSF32 inActualAspect) +{ + return (inMode == CameraScaleModes::Fit && inActualAspect < inDesignAspect) + || inMode == CameraScaleModes::FitHorizontal; +} + +bool IsFitTypeScaleMode(CameraScaleModes::Enum inMode) +{ + return inMode == CameraScaleModes::Fit || inMode == CameraScaleModes::FitHorizontal + || inMode == CameraScaleModes::FitVertical; +} + +struct SPinCameraResult +{ + NVRenderRectF m_Viewport; + NVRenderRectF m_VirtualViewport; + SPinCameraResult(NVRenderRectF v, NVRenderRectF vv) + : m_Viewport(v) + , m_VirtualViewport(vv) + { + } +}; +// Scale and transform the projection matrix to respect the camera anchor attribute +// and the scale mode. +SPinCameraResult PinCamera(const NVRenderRectF &inViewport, QT3DSVec2 inDesignDims, + QT3DSMat44 &ioPerspectiveMatrix, CameraScaleModes::Enum inScaleMode, + CameraScaleAnchors::Enum inPinLocation) +{ + NVRenderRectF viewport(inViewport); + NVRenderRectF idealViewport(inViewport.m_X, inViewport.m_Y, inDesignDims.x, inDesignDims.y); + QT3DSF32 designAspect = GetAspectRatio(inDesignDims); + QT3DSF32 actualAspect = GetAspectRatio(inViewport); + if (IsFitTypeScaleMode(inScaleMode)) { + idealViewport.m_Width = viewport.m_Width; + idealViewport.m_Height = viewport.m_Height; + } + // We move the viewport such that the left, top of the presentation sits against the left top + // edge + // We only need to translate in X *if* our actual aspect > design aspect + // And then we only need to account for whatever centering would happen. + + bool pinLeft = inPinLocation == CameraScaleAnchors::SouthWest + || inPinLocation == CameraScaleAnchors::West + || inPinLocation == CameraScaleAnchors::NorthWest; + bool pinRight = inPinLocation == CameraScaleAnchors::SouthEast + || inPinLocation == CameraScaleAnchors::East + || inPinLocation == CameraScaleAnchors::NorthEast; + bool pinTop = inPinLocation == CameraScaleAnchors::NorthWest + || inPinLocation == CameraScaleAnchors::North + || inPinLocation == CameraScaleAnchors::NorthEast; + bool pinBottom = inPinLocation == CameraScaleAnchors::SouthWest + || inPinLocation == CameraScaleAnchors::South + || inPinLocation == CameraScaleAnchors::SouthEast; + + if (inScaleMode == CameraScaleModes::SameSize) { + // In this case the perspective transform does not center the view, + // it places it in the lower-left of the viewport. + QT3DSF32 idealWidth = inDesignDims.x; + QT3DSF32 idealHeight = inDesignDims.y; + if (pinRight) + idealViewport.m_X -= ((idealWidth - inViewport.m_Width)); + else if (!pinLeft) + idealViewport.m_X -= ((idealWidth - inViewport.m_Width) / 2.0f); + + if (pinTop) + idealViewport.m_Y -= ((idealHeight - inViewport.m_Height)); + else if (!pinBottom) + idealViewport.m_Y -= ((idealHeight - inViewport.m_Height) / 2.0f); + } else { + // In this case our perspective matrix will center the view and we need to decenter + // it as necessary + // if we are wider than we are high + if (IsCameraVerticalAdjust(inScaleMode, designAspect, actualAspect)) { + if (pinLeft || pinRight) { + QT3DSF32 idealWidth = inViewport.m_Height * designAspect; + QT3DSI32 halfOffset = (QT3DSI32)((idealWidth - inViewport.m_Width) / 2.0f); + halfOffset = pinLeft ? halfOffset : -1 * halfOffset; + idealViewport.m_X += halfOffset; + } + } else { + if (pinTop || pinBottom) { + QT3DSF32 idealHeight = inViewport.m_Width / designAspect; + QT3DSI32 halfOffset = (QT3DSI32)((idealHeight - inViewport.m_Height) / 2.0f); + halfOffset = pinBottom ? halfOffset : -1 * halfOffset; + idealViewport.m_Y += halfOffset; + } + } + } + + ioPerspectiveMatrix = NVRenderContext::ApplyVirtualViewportToProjectionMatrix( + ioPerspectiveMatrix, viewport, idealViewport); + return SPinCameraResult(viewport, idealViewport); +} +} + +SCamera::SCamera() + : SNode(GraphObjectTypes::Camera) + , m_ClipNear(10) + , m_ClipFar(10000) + , m_FOV(60) + , m_FOVHorizontal(false) + , m_ScaleMode(CameraScaleModes::Fit) + , m_ScaleAnchor(CameraScaleAnchors::Center) +{ + TORAD(m_FOV); + m_Projection = QT3DSMat44::createIdentity(); + m_Position = QT3DSVec3(0, 0, -600); +} + +// Code for testing +SCameraGlobalCalculationResult SCamera::CalculateGlobalVariables(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) +{ + bool wasDirty = SNode::CalculateGlobalVariables(); + return SCameraGlobalCalculationResult(wasDirty, + CalculateProjection(inViewport, inDesignDimensions)); +} + +bool SCamera::CalculateProjection(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions) +{ + bool retval = false; + if (m_Flags.IsOrthographic()) + retval = ComputeFrustumOrtho(inViewport, inDesignDimensions); + else + retval = ComputeFrustumPerspective(inViewport, inDesignDimensions); + if (retval) { + QT3DSF32 *writePtr(m_Projection.front()); + m_FrustumScale.x = writePtr[0]; + m_FrustumScale.y = writePtr[5]; + PinCamera(inViewport, inDesignDimensions, m_Projection, m_ScaleMode, m_ScaleAnchor); + } + return retval; +} + +//============================================================================== +/** + * Compute the projection matrix for a perspective camera + * @return true if the computed projection matrix is valid + */ +bool SCamera::ComputeFrustumPerspective(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) +{ + m_Projection = QT3DSMat44::createIdentity(); + QT3DSF32 theAngleInRadians = verticalFov(inViewport) / 2.0f; + QT3DSF32 theDeltaZ = m_ClipFar - m_ClipNear; + QT3DSF32 theSine = sinf(theAngleInRadians); + QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions); + QT3DSF32 theAspectRatio = designAspect; + if (IsFitTypeScaleMode(m_ScaleMode)) + theAspectRatio = GetAspectRatio(inViewport); + + if ((theDeltaZ != 0) && (theSine != 0) && (theAspectRatio != 0)) { + QT3DSF32 *writePtr(m_Projection.front()); + writePtr[10] = -(m_ClipFar + m_ClipNear) / theDeltaZ; + writePtr[11] = -1; + writePtr[14] = -2 * m_ClipNear * m_ClipFar / theDeltaZ; + writePtr[15] = 0; + + if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, theAspectRatio)) { + QT3DSF32 theCotangent = cosf(theAngleInRadians) / theSine; + writePtr[0] = theCotangent / theAspectRatio; + writePtr[5] = theCotangent; + } else { + QT3DSF32 theCotangent = cosf(theAngleInRadians) / theSine; + writePtr[0] = theCotangent / designAspect; + writePtr[5] = theCotangent * (theAspectRatio / designAspect); + } + return true; + } else { + QT3DS_ASSERT(false); + return false; + } +} + +//============================================================================== +/** + * Compute the projection matrix for a orthographic camera + * @return true if the computed projection matrix is valid + */ +bool SCamera::ComputeFrustumOrtho(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions) +{ + m_Projection = QT3DSMat44::createIdentity(); + + QT3DSF32 theDeltaZ = m_ClipFar - m_ClipNear; + QT3DSF32 halfWidth = inDesignDimensions.x / 2.0f; + QT3DSF32 halfHeight = inDesignDimensions.y / 2.0f; + QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions); + QT3DSF32 theAspectRatio = designAspect; + if (IsFitTypeScaleMode(m_ScaleMode)) + theAspectRatio = GetAspectRatio(inViewport); + if (theDeltaZ != 0) { + QT3DSF32 *writePtr(m_Projection.front()); + writePtr[10] = -2.0f / theDeltaZ; + writePtr[11] = 0.0f; + writePtr[14] = -(m_ClipNear + m_ClipFar) / theDeltaZ; + writePtr[15] = 1.0f; + if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, theAspectRatio)) { + writePtr[0] = 1.0f / (halfHeight * theAspectRatio); + writePtr[5] = 1.0f / halfHeight; + } else { + writePtr[0] = 1.0f / halfWidth; + writePtr[5] = 1.0f / (halfWidth / theAspectRatio); + } + return true; + } else { + QT3DS_ASSERT(false); + return false; + } +} + +QT3DSF32 SCamera::GetOrthographicScaleFactor(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const +{ + if (m_ScaleMode == CameraScaleModes::SameSize) + return 1.0f; + QT3DSMat44 temp(QT3DSMat44::createIdentity()); + QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions); + QT3DSF32 theAspectRatio = GetAspectRatio(inViewport); + if (m_ScaleMode == CameraScaleModes::Fit) { + if (theAspectRatio >= designAspect) { + return inViewport.m_Width < inDesignDimensions.x ? theAspectRatio / designAspect : 1.0f; + + } else { + return inViewport.m_Height < inDesignDimensions.y ? designAspect / theAspectRatio + : 1.0f; + } + } else if (m_ScaleMode == CameraScaleModes::FitVertical) { + return (QT3DSF32)inDesignDimensions.y / (QT3DSF32)inViewport.m_Height; + } else { + return (QT3DSF32)inDesignDimensions.x / (QT3DSF32)inViewport.m_Width; + } +} + +QT3DSF32 SCamera::GetTextScaleFactor(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const +{ + return NVMax(1.0f, 1.0f / GetOrthographicScaleFactor(inViewport, inDesignDimensions)); +} + +QT3DSMat33 SCamera::GetLookAtMatrix(const QT3DSVec3 &inUpDir, const QT3DSVec3 &inDirection) const +{ + QT3DSVec3 theDirection(inDirection); + + theDirection.normalize(); + + const QT3DSVec3 &theUpDir(inUpDir); + + // gram-shmidt orthogonalization + QT3DSVec3 theCrossDir(theDirection.cross(theUpDir)); + theCrossDir.normalize(); + QT3DSVec3 theFinalDir(theCrossDir.cross(theDirection)); + theFinalDir.normalize(); + QT3DSF32 multiplier = 1.0f; + if (m_Flags.IsLeftHanded()) + multiplier = -1.0f; + + QT3DSMat33 theResultMatrix(theCrossDir, theFinalDir, multiplier * theDirection); + return theResultMatrix; +} + +void SCamera::LookAt(const QT3DSVec3 &inCameraPos, const QT3DSVec3 &inUpDir, const QT3DSVec3 &inTargetPos) +{ + QT3DSVec3 theDirection = inTargetPos - inCameraPos; + if (m_Flags.IsLeftHanded()) + theDirection.z *= -1.0f; + m_Rotation = GetRotationVectorFromRotationMatrix(GetLookAtMatrix(inUpDir, theDirection)); + m_Position = inCameraPos; + MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); +} + +void SCamera::CalculateViewProjectionMatrix(QT3DSMat44 &outMatrix) const +{ + QT3DSMat44 globalInverse = m_GlobalTransform.getInverse(); + outMatrix = m_Projection * globalInverse; +} + +SCuboidRect SCamera::GetCameraBounds(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const +{ + QT3DSMat44 unused(QT3DSMat44::createIdentity()); + SPinCameraResult theResult = + PinCamera(inViewport, inDesignDimensions, unused, m_ScaleMode, m_ScaleAnchor); + // find the normalized edges of the view frustum given the renormalization that happens when + // pinning the camera. + SCuboidRect normalizedCuboid(-1, 1, 1, -1); + QT3DSVec2 translation(theResult.m_Viewport.m_X - theResult.m_VirtualViewport.m_X, + theResult.m_Viewport.m_Y - theResult.m_VirtualViewport.m_Y); + if (m_ScaleMode == CameraScaleModes::SameSize) { + // the cuboid ranges are the actual divided by the ideal in this case + QT3DSF32 xRange = 2.0f * (theResult.m_Viewport.m_Width / theResult.m_VirtualViewport.m_Width); + QT3DSF32 yRange = + 2.0f * (theResult.m_Viewport.m_Height / theResult.m_VirtualViewport.m_Height); + normalizedCuboid = SCuboidRect(-1, -1 + yRange, -1 + xRange, -1); + translation.x /= (theResult.m_VirtualViewport.m_Width / 2.0f); + translation.y /= (theResult.m_VirtualViewport.m_Height / 2.0f); + normalizedCuboid.Translate(translation); + } + // fit. This means that two parameters of the normalized cuboid will be -1, 1. + else { + // In this case our perspective matrix will center the view and we need to decenter + // it as necessary + QT3DSF32 actualAspect = GetAspectRatio(inViewport); + QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions); + // if we are wider than we are high + QT3DSF32 idealWidth = inViewport.m_Width; + QT3DSF32 idealHeight = inViewport.m_Height; + + if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, actualAspect)) { + // then we just need to setup the left, right parameters of the cuboid because we know + // the top + // bottom are -1,1 due to how fit works. + idealWidth = (QT3DSF32)ITextRenderer::NextMultipleOf4( + (QT3DSU32)(inViewport.m_Height * designAspect + .5f)); + // halfRange should always be greater than 1.0f. + QT3DSF32 halfRange = inViewport.m_Width / idealWidth; + normalizedCuboid.m_Left = -halfRange; + normalizedCuboid.m_Right = halfRange; + translation.x = translation.x / (idealWidth / 2.0f); + } else { + idealHeight = (QT3DSF32)ITextRenderer::NextMultipleOf4( + (QT3DSU32)(inViewport.m_Width / designAspect + .5f)); + QT3DSF32 halfRange = inViewport.m_Height / idealHeight; + normalizedCuboid.m_Bottom = -halfRange; + normalizedCuboid.m_Top = halfRange; + translation.y = translation.y / (idealHeight / 2.0f); + } + normalizedCuboid.Translate(translation); + } + // Given no adjustment in the virtual rect, then this is what we would have. + + return normalizedCuboid; +} + +void SCamera::SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture, + QT3DSMat44 &outVP) +{ + STextureDetails theDetails(inTexture.GetTextureDetails()); + SCamera theTempCamera; + SetupOrthographicCameraForOffscreenRender(inTexture, outVP, theTempCamera); +} + +void SCamera::SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture, + QT3DSMat44 &outVP, SCamera &outCamera) +{ + STextureDetails theDetails(inTexture.GetTextureDetails()); + SCamera theTempCamera; + theTempCamera.m_Flags.SetOrthographic(true); + theTempCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + QT3DSVec2 theDimensions((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height); + theTempCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theDetails.m_Width, theDetails.m_Height), theDimensions); + theTempCamera.CalculateViewProjectionMatrix(outVP); + outCamera = theTempCamera; +} + +SRay SCamera::Unproject(const QT3DSVec2 &inViewportRelativeCoords, const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions, bool sceneCameraView) const +{ + SRay theRay; + QT3DSMat44 tempVal(QT3DSMat44::createIdentity()); + SPinCameraResult result = + PinCamera(inViewport, inDesignDimensions, tempVal, m_ScaleMode, m_ScaleAnchor); + QT3DSVec2 globalCoords = inViewport.ToAbsoluteCoords(inViewportRelativeCoords); + QT3DSVec2 normalizedCoords = + result.m_VirtualViewport.AbsoluteToNormalizedCoordinates(globalCoords); + QT3DSVec3 &outOrigin(theRay.m_Origin); + QT3DSVec3 &outDir(theRay.m_Direction); + QT3DSVec2 inverseFrustumScale(1.0f / m_FrustumScale.x, 1.0f / m_FrustumScale.y); + QT3DSVec2 scaledCoords(inverseFrustumScale.x * normalizedCoords.x, + inverseFrustumScale.y * normalizedCoords.y); + + if (m_Flags.IsOrthographic()) { + outOrigin.x = scaledCoords.x; + outOrigin.y = scaledCoords.y; + outOrigin.z = 0.0f; + + outDir.x = 0.0f; + outDir.y = 0.0f; + outDir.z = -1.0f; + } else { + outOrigin.x = 0.0f; + outOrigin.y = 0.0f; + outOrigin.z = 0.0f; + + outDir.x = scaledCoords.x; + outDir.y = scaledCoords.y; + outDir.z = -1.0f; + } + + outOrigin = m_GlobalTransform.transform(outOrigin); + + // CalculateNormalMatrix(), but 4x4 matrix to have scale() method + QT3DSMat44 theNormalMatrix = m_GlobalTransform.getInverse().getTranspose(); + if (sceneCameraView) { + // When in scene camera view mode, camera scale needs to be inverted. + // See QT3DS-3393. + const float scaleX = m_GlobalTransform[0][0] / theNormalMatrix[0][0]; + const float scaleY = m_GlobalTransform[1][1] / theNormalMatrix[1][1]; + const float scaleZ = m_GlobalTransform[2][2] / theNormalMatrix[2][2]; + QT3DSVec4 scaleVector(scaleX, scaleY, scaleZ, 1.0); + theNormalMatrix.scale(scaleVector); + } + + outDir = theNormalMatrix.transform(outDir); + outDir.normalize(); + /* + char printBuf[2000]; + sprintf_s( printBuf, "normCoords %f %f outDir %f %f %f\n" + , normalizedCoords.x, normalizedCoords.y, outDir.x, outDir.y, outDir.z ); + OutputDebugStringA( printBuf ); + */ + + return theRay; +} + +QT3DSVec3 SCamera::UnprojectToPosition(const QT3DSVec3 &inGlobalPos, const SRay &inRay) const +{ + QT3DSVec3 theCameraDir = GetDirection(); + QT3DSVec3 theObjGlobalPos = inGlobalPos; + QT3DSF32 theDistance = -1.0f * theObjGlobalPos.dot(theCameraDir); + NVPlane theCameraPlane(theCameraDir, theDistance); + return inRay.Intersect(theCameraPlane); +} + +QT3DSF32 SCamera::verticalFov(QT3DSF32 aspectRatio) const +{ + if (m_FOVHorizontal) + return 2.0f * qAtan(qTan(qreal(m_FOV) / 2.0) / qreal(aspectRatio)); + else + return m_FOV; +} + +QT3DSF32 SCamera::verticalFov(const NVRenderRectF &inViewport) const +{ + return verticalFov(GetAspectRatio(inViewport)); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderCamera.h b/src/runtimerender/graphobjects/Qt3DSRenderCamera.h new file mode 100644 index 0000000..be8d53e --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderCamera.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CAMERA_H +#define QT3DS_RENDER_CAMERA_H +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderRay.h" + +namespace qt3ds { +namespace render { + + struct SCameraGlobalCalculationResult + { + bool m_WasDirty; + bool m_ComputeFrustumSucceeded; + SCameraGlobalCalculationResult(bool inWasDirty, bool inComputeSucceeded = true) + : m_WasDirty(inWasDirty) + , m_ComputeFrustumSucceeded(inComputeSucceeded) + { + } + }; + + struct CameraScaleModes + { + enum Enum { + Fit = 0, + SameSize, + FitHorizontal, + FitVertical, + }; + }; + + struct CameraScaleAnchors + { + enum Enum { + Center = 0, + North, + NorthEast, + East, + SouthEast, + South, + SouthWest, + West, + NorthWest, + }; + }; + + struct SCuboidRect + { + QT3DSF32 m_Left; + QT3DSF32 m_Top; + QT3DSF32 m_Right; + QT3DSF32 m_Bottom; + SCuboidRect(QT3DSF32 l = 0.0f, QT3DSF32 t = 0.0f, QT3DSF32 r = 0.0f, QT3DSF32 b = 0.0f) + : m_Left(l) + , m_Top(t) + , m_Right(r) + , m_Bottom(b) + { + } + void Translate(QT3DSVec2 inTranslation) + { + m_Left += inTranslation.x; + m_Right += inTranslation.x; + m_Top += inTranslation.y; + m_Bottom += inTranslation.y; + } + }; + + struct SCamera : public SNode + { + + // Setting these variables should set dirty on the camera. + QT3DSF32 m_ClipNear; + QT3DSF32 m_ClipFar; + + QT3DSF32 m_FOV; // Radians + bool m_FOVHorizontal; + + QT3DSMat44 m_Projection; + CameraScaleModes::Enum m_ScaleMode; + CameraScaleAnchors::Enum m_ScaleAnchor; + // Record some values from creating the projection matrix + // to use during mouse picking. + QT3DSVec2 m_FrustumScale; + + SCamera(); + + QT3DSMat33 GetLookAtMatrix(const QT3DSVec3 &inUpDir, const QT3DSVec3 &inDirection) const; + // Set our position, rotation member variables based on the lookat target + // Marks this object as dirty. + // Need to test this when the camera's local transform is null. + // Assumes parent's local transform is the identity, meaning our local transform is + // our global transform. + void LookAt(const QT3DSVec3 &inCameraPos, const QT3DSVec3 &inUpDir, const QT3DSVec3 &inTargetPos); + + SCameraGlobalCalculationResult CalculateGlobalVariables(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions); + bool CalculateProjection(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions); + bool ComputeFrustumOrtho(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions); + // Used when rendering the widgets in studio. This scales the widget when in orthographic + // mode in order to have + // constant size on screen regardless. + // Number is always greater than one + QT3DSF32 GetOrthographicScaleFactor(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const; + bool ComputeFrustumPerspective(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions); + // Text may be scaled so that it doesn't appear pixellated when the camera itself is doing + // the scaling. + QT3DSF32 GetTextScaleFactor(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const; + + void CalculateViewProjectionMatrix(QT3DSMat44 &outMatrix) const; + + // If this is an orthographic camera, the cuboid properties are the distance from the center + // point + // to the left, top, right, and bottom edges of the view frustum in world units. + // If this is a perspective camera, the cuboid properties are the FOV angles + // (left,top,right,bottom) + // of the view frustum. + + // Return a normalized rect that describes the area the camera is rendering to. + // This takes into account the various camera properties (scale mode, scale anchor). + SCuboidRect GetCameraBounds(const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions) const; + + // Setup a camera VP projection for rendering offscreen. + static void SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture, + QT3DSMat44 &outVP); + static void SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture, + QT3DSMat44 &outVP, SCamera &outCamera); + + // Unproject a point (x,y) in viewport relative coordinates meaning + // left, bottom is 0,0 and values are increasing right,up respectively. + SRay Unproject(const QT3DSVec2 &inLayerRelativeMouseCoords, const NVRenderRectF &inViewport, + const QT3DSVec2 &inDesignDimensions, bool sceneCameraView = false) const; + + // Unproject a given coordinate to a 3d position that lies on the same camera + // plane as inGlobalPos. + // Expects CalculateGlobalVariables has been called or doesn't need to be. + QT3DSVec3 UnprojectToPosition(const QT3DSVec3 &inGlobalPos, const SRay &inRay) const; + + QT3DSF32 verticalFov(QT3DSF32 aspectRatio) const; + QT3DSF32 verticalFov(const NVRenderRectF &inViewport) const; + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderCustomMaterial.h b/src/runtimerender/graphobjects/Qt3DSRenderCustomMaterial.h new file mode 100644 index 0000000..593c757 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderCustomMaterial.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_CUSTOM_MATERIAL_H +#define QT3DS_RENDER_CUSTOM_MATERIAL_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderDynamicObject.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderLightmaps.h" +#include "foundation/Qt3DSFlags.h" + +namespace qt3ds { +namespace render { + + // IMPORTANT: These flags matches the key produced by a MDL export file + struct SCustomMaterialShaderKeyValues + { + enum Enum { + diffuse = 1 << 0, + specular = 1 << 1, + glossy = 1 << 2, + cutout = 1 << 3, + refraction = 1 << 4, + transparent = 1 << 5, + displace = 1 << 6, + volumetric = 1 << 7, + transmissive = 1 << 8, + }; + }; + + typedef NVFlags<SCustomMaterialShaderKeyValues::Enum, QT3DSU32> SCustomMaterialShaderKeyFlags; + + struct SCustomMaterial : public SDynamicObject + { + private: + // These objects are only created via the dynamic object system. + SCustomMaterial(const SCustomMaterial &); + SCustomMaterial &operator=(const SCustomMaterial &); + SCustomMaterial(); + + public: + // lightmap section + SLightmaps m_Lightmaps; + // material section + bool m_hasTransparency; + bool m_hasRefraction; + bool m_hasVolumetricDF; + SImage *m_IblProbe; + SImage *m_EmissiveMap2; + SImage *m_DisplacementMap; + QT3DSF32 m_DisplaceAmount; ///< depends on the object size + + SGraphObject *m_NextSibling; + + SCustomMaterialShaderKeyFlags m_ShaderKeyValues; ///< input from MDL files + QT3DSU32 m_LayerCount; ///< input from MDL files + + void Initialize(QT3DSU32 inKey, QT3DSU32 inLayerCount) + { + m_Lightmaps.m_LightmapIndirect = NULL; + m_Lightmaps.m_LightmapRadiosity = NULL; + m_Lightmaps.m_LightmapShadow = NULL; + m_hasTransparency = false; + m_hasRefraction = false; + m_hasVolumetricDF = false; + m_NextSibling = NULL; + m_DirtyFlagWithInFrame = m_Flags.IsDirty(); + m_IblProbe = NULL; + m_EmissiveMap2 = NULL; + m_DisplacementMap = NULL; + m_DisplaceAmount = 0.0; + m_ShaderKeyValues = (SCustomMaterialShaderKeyFlags)inKey; + m_LayerCount = inLayerCount; + } + + bool IsDielectric() const + { + return m_ShaderKeyValues & SCustomMaterialShaderKeyValues::diffuse; + } + bool IsSpecularEnabled() const + { + return m_ShaderKeyValues & SCustomMaterialShaderKeyValues::specular; + } + bool IsCutOutEnabled() const + { + return m_ShaderKeyValues & SCustomMaterialShaderKeyValues::cutout; + } + bool IsVolumetric() const + { + return m_ShaderKeyValues & SCustomMaterialShaderKeyValues::volumetric; + } + bool IsTransmissive() const + { + return m_ShaderKeyValues & SCustomMaterialShaderKeyValues::transmissive; + } + bool HasLighting() const { return true; } + + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SDynamicObject::Remap(inRemapper); + m_Lightmaps.Remap(inRemapper); + inRemapper.Remap(m_IblProbe); + inRemapper.RemapMaterial(m_NextSibling); + inRemapper.Remap(m_EmissiveMap2); + inRemapper.Remap(m_DisplacementMap); + } + + // Dirty + bool m_DirtyFlagWithInFrame; + bool IsDirty() const { return m_Flags.IsDirty() || m_DirtyFlagWithInFrame; } + void UpdateDirtyForFrame() + { + m_DirtyFlagWithInFrame = m_Flags.IsDirty(); + m_Flags.SetDirty(false); + } + }; +} +} +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.cpp b/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.cpp new file mode 100644 index 0000000..e18a84d --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderDefaultMaterial.h" + +using namespace qt3ds::render; + +SDefaultMaterial::SDefaultMaterial() + : SGraphObject(GraphObjectTypes::DefaultMaterial) + , m_IblProbe(NULL) + , m_Lighting(DefaultMaterialLighting::VertexLighting) + , m_BlendMode(DefaultMaterialBlendMode::Normal) + , m_DiffuseColor(1, 1, 1, 1) + , m_EmissivePower(0) + , m_EmissiveMap(NULL) + , m_EmissiveMap2(NULL) + , m_EmissiveColor(1, 1, 1, 1) + , m_SpecularReflection(NULL) + , m_SpecularMap(NULL) + , m_SpecularModel(DefaultMaterialSpecularModel::Default) + , m_SpecularTint(1, 1, 1, 1) + , m_IOR(.2f) + , m_FresnelPower(0.0f) + , m_SpecularAmount(0) + , m_SpecularRoughness(50) + , m_RoughnessMap(NULL) + , m_Opacity(1) + , m_OpacityMap(NULL) + , m_BumpMap(NULL) + , m_BumpAmount(0.f) + , m_NormalMap(NULL) + , m_DisplacementMap(NULL) + , m_DisplaceAmount(0.f) + , m_TranslucencyMap(NULL) + , m_TranslucentFalloff(0.f) + , m_DiffuseLightWrap(0.f) + , m_VertexColors(false) + , m_NextSibling(NULL) + , m_Parent(NULL) +{ + m_Lightmaps.m_LightmapIndirect = NULL; + m_Lightmaps.m_LightmapRadiosity = NULL; + m_Lightmaps.m_LightmapShadow = NULL; + + m_DiffuseMaps[0] = NULL; + m_DiffuseMaps[2] = NULL; + m_DiffuseMaps[1] = NULL; +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.h b/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.h new file mode 100644 index 0000000..48c8d0b --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2015 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_DEFAULT_MATERIAL_H +#define QT3DS_RENDER_DEFAULT_MATERIAL_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSVec3.h" +#include "Qt3DSRenderMaterialDirty.h" +#include "Qt3DSRenderLightmaps.h" + +namespace qt3ds { +namespace render { + struct DefaultMaterialLighting + { + enum Enum { + NoLighting = 0, + VertexLighting, + FragmentLighting + }; + }; + struct DefaultMaterialBlendMode + { + enum Enum { + Normal = 0, + Screen, + Multiply, + Overlay, + ColorBurn, + ColorDodge + }; + }; + + struct DefaultMaterialSpecularModel + { + enum Enum { + Default = 0, + KGGX, + KWard + }; + }; + + struct SImage; + + struct QT3DS_AUTOTEST_EXPORT SDefaultMaterial : SGraphObject + { + CMaterialDirty m_Dirty; + // lightmap section + SLightmaps m_Lightmaps; + // material section + SImage *m_IblProbe; + DefaultMaterialLighting::Enum m_Lighting; // defaults to vertex + DefaultMaterialBlendMode::Enum m_BlendMode; // defaults to normal + QT3DSVec4 m_DiffuseColor; // colors are 0-1 normalized + SImage *m_DiffuseMaps[3]; + QT3DSF32 m_EmissivePower; // 0-100, defaults to 0 + QT3DSVec4 m_EmissiveColor; + SImage *m_EmissiveMap; + SImage *m_EmissiveMap2; + SImage *m_SpecularReflection; + SImage *m_SpecularMap; + DefaultMaterialSpecularModel::Enum m_SpecularModel; + QT3DSVec4 m_SpecularTint; + QT3DSF32 m_IOR; + QT3DSF32 m_FresnelPower; + QT3DSF32 m_SpecularAmount; // 0-??, defaults to 0 + QT3DSF32 m_SpecularRoughness; // 0-??, defaults to 50 + SImage *m_RoughnessMap; + QT3DSF32 m_Opacity; // 0-1 + SImage *m_OpacityMap; + SImage *m_BumpMap; + QT3DSF32 m_BumpAmount; // 0-?? + SImage *m_NormalMap; + SImage *m_DisplacementMap; + QT3DSF32 m_DisplaceAmount; // 0-?? + SImage *m_TranslucencyMap; + QT3DSF32 m_TranslucentFalloff; // 0 - ?? + QT3DSF32 m_DiffuseLightWrap; // 0 - 1 + bool m_VertexColors; + // Materials are stored as a linked list on models. + SGraphObject *m_NextSibling; + SModel *m_Parent; + + SDefaultMaterial(); + + bool IsSpecularEnabled() const { return m_SpecularAmount > .01f; } + bool IsFresnelEnabled() const { return m_FresnelPower > 0.0f; } + bool IsVertexColorsEnabled() const { return m_VertexColors; } + bool HasLighting() const { return m_Lighting != DefaultMaterialLighting::NoLighting; } + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + m_Lightmaps.Remap(inRemapper); + inRemapper.Remap(m_IblProbe); + inRemapper.Remap(m_DiffuseMaps[0]); + inRemapper.Remap(m_DiffuseMaps[1]); + inRemapper.Remap(m_DiffuseMaps[2]); + inRemapper.Remap(m_EmissiveMap); + inRemapper.Remap(m_EmissiveMap2); + inRemapper.Remap(m_SpecularReflection); + inRemapper.Remap(m_SpecularMap); + inRemapper.Remap(m_RoughnessMap); + inRemapper.Remap(m_OpacityMap); + inRemapper.Remap(m_BumpMap); + inRemapper.Remap(m_NormalMap); + inRemapper.Remap(m_DisplacementMap); + inRemapper.Remap(m_TranslucencyMap); + inRemapper.RemapMaterial(m_NextSibling); + inRemapper.Remap(m_Parent); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.cpp b/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.cpp new file mode 100644 index 0000000..2e1e6a0 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "Qt3DSRenderDynamicObject.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "foundation/FileTools.h" +#include "StringTools.h" + +#include <QtCore/qdir.h> + +using namespace qt3ds; +using namespace qt3ds::render; +using namespace qt3ds::foundation; + +SDynamicObject::SDynamicObject(GraphObjectTypes::Enum inType, CRegisteredString inObjName, + QT3DSU32 inDSByteSize, QT3DSU32 thisObjSize) + : SGraphObject(inType) + , m_ClassName(inObjName) + , m_DataSectionByteSize(inDSByteSize) + , m_ThisObjectSize(thisObjSize) +{ +} + +template <typename TDataType> +void SDynamicObject::SetPropertyValueT(const dynamic::SPropertyDefinition &inDefinition, + const TDataType &inValue) +{ + if (sizeof(inValue) != inDefinition.m_ByteSize) { + QT3DS_ASSERT(false); + return; + } + memCopy(GetDataSectionBegin() + inDefinition.m_Offset, &inValue, sizeof(inValue)); +} + +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + bool inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} + +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + QT3DSF32 inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + QT3DSF32 inValue, QT3DSU32 inOffset) +{ + if (sizeof(QT3DSF32) > (inDefinition.m_ByteSize - inOffset)) { + QT3DS_ASSERT(false); + return; + } + memCopy(GetDataSectionBegin() + inDefinition.m_Offset + inOffset, &inValue, sizeof(inValue)); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec2 &inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec3 &inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec4 &inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + QT3DSI32 inValue) +{ + SetPropertyValueT(inDefinition, inValue); +} +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + CRegisteredString inValue) +{ + QT3DS_ASSERT(inDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr); + SetPropertyValueT(inDefinition, inValue); +} + +template <typename TStrType> +void SDynamicObject::SetStrPropertyValueT(dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + TStrType &ioWorkspace, IStringTable &inStrTable) +{ + if (inValue == NULL) + inValue = ""; + if (inDefinition.m_DataType == NVRenderShaderDataTypes::QT3DSI32) { + NVConstDataRef<CRegisteredString> theEnumValues = inDefinition.m_EnumValueNames; + for (QT3DSI32 idx = 0, end = (QT3DSI32)theEnumValues.size(); idx < end; ++idx) { + if (strcmp(theEnumValues[idx].c_str(), inValue) == 0) { + SetPropertyValueT(inDefinition, idx); + break; + } + } + } else if (inDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + if (inProjectDir == NULL) + inProjectDir = ""; + if (CFileTools::RequiresCombineBaseAndRelative(inValue)) { + QString absolute = QDir(inProjectDir).filePath(inValue); + ioWorkspace.assign(absolute.toLatin1().constData()); + SetPropertyValueT(inDefinition, inStrTable.RegisterStr(ioWorkspace.c_str())); + // We also adjust the image path in the definition + // I could not find a better place + inDefinition.m_ImagePath = inStrTable.RegisterStr(ioWorkspace.c_str()); + } else { + SetPropertyValueT(inDefinition, inStrTable.RegisterStr(inValue)); + } + } else if (inDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderImage2DPtr) { + SetPropertyValueT(inDefinition, inStrTable.RegisterStr(inValue)); + } else if (inDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderDataBufferPtr) { + SetPropertyValueT(inDefinition, inStrTable.RegisterStr(inValue)); + } else { + QT3DS_ASSERT(false); + } +} + +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + Qt3DSString &ioWorkspace, IStringTable &inStrTable) +{ + SetStrPropertyValueT(const_cast<dynamic::SPropertyDefinition &>(inDefinition), inValue, + inProjectDir, ioWorkspace, inStrTable); +} + +void SDynamicObject::SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + eastl::string &ioWorkspace, IStringTable &inStrTable) +{ + SetStrPropertyValueT(const_cast<dynamic::SPropertyDefinition &>(inDefinition), inValue, + inProjectDir, ioWorkspace, inStrTable); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.h b/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.h new file mode 100644 index 0000000..92ab3b2 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_DYNAMIC_OBJECT_H +#define QT3DS_RENDER_DYNAMIC_OBJECT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "Qt3DSRenderNode.h" +#include "EASTL/string.h" +#include "StringTools.h" + +namespace qt3ds { +namespace render { + + namespace dynamic { + struct SPropertyDefinition; + } + + // Dynamic objects are objects that have variable number of properties during runtime. + struct SDynamicObject : public SGraphObject + { + CRegisteredString m_ClassName; + NodeFlags m_Flags; + QT3DSU32 m_DataSectionByteSize; + QT3DSU32 m_ThisObjectSize; + + SDynamicObject(GraphObjectTypes::Enum inType, CRegisteredString inClassName, + QT3DSU32 inDSByteSize, QT3DSU32 thisObjSize); + + QT3DSU8 *GetDataSectionBegin() + { + QT3DSU8 *thisObjectStart = reinterpret_cast<QT3DSU8 *>(this); + QT3DSU8 *retval = thisObjectStart + m_ThisObjectSize; + QT3DS_ASSERT((reinterpret_cast<size_t>(retval) % 4 == 0)); + return retval; + } + + const QT3DSU8 *GetDataSectionBegin() const + { + return const_cast<SDynamicObject *>(this)->GetDataSectionBegin(); + } + + QT3DSU8 *GetDataSectionEnd() { return GetDataSectionBegin() + m_DataSectionByteSize; } + + template <typename TDataType> + void SetPropertyValueT(const dynamic::SPropertyDefinition &inDefinition, + const TDataType &inType); + template <typename TStrType> + void SetStrPropertyValueT(dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + TStrType &ioWorkspace, IStringTable &inStrTable); + + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, bool inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, QT3DSF32 inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, QT3DSF32 inValue, + QT3DSU32 inOffset); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec2 &inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec3 &inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const QT3DSVec4 &inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, QT3DSI32 inValue); + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + CRegisteredString inValue); + + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + Qt3DSString &ioWorkspace, IStringTable &inStrTable); + + void SetPropertyValue(const dynamic::SPropertyDefinition &inDefinition, + const char8_t *inValue, const char8_t *inProjectDir, + eastl::string &ioWorkspace, IStringTable &inStrTable); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_ClassName); + } + }; +} +} +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderEffect.cpp b/src/runtimerender/graphobjects/Qt3DSRenderEffect.cpp new file mode 100644 index 0000000..f074b6b --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderEffect.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderEffect.h" +#include "Qt3DSRenderEffectSystem.h" +#include "foundation/Qt3DSVec2.h" +#include "foundation/Qt3DSVec3.h" +#include "StringTools.h" +#include "foundation/FileTools.h" + +using namespace qt3ds::render; + +void SEffect::Initialize() +{ + m_Layer = NULL; + m_NextEffect = NULL; + m_Context = NULL; +} + +void SEffect::SetActive(bool inActive, IEffectSystem &inManager) +{ + if (m_Flags.IsActive() != inActive) { + m_Flags.SetActive(inActive); + if (m_Context) + inManager.ResetEffectFrameData(*m_Context); + m_Flags.SetDirty(true); + } +} + +void SEffect::Reset(IEffectSystem &inSystem) +{ + if (m_Context) + inSystem.ResetEffectFrameData(*m_Context); + m_Flags.SetDirty(true); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderEffect.h b/src/runtimerender/graphobjects/Qt3DSRenderEffect.h new file mode 100644 index 0000000..c11c214 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderEffect.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_EFFECT_H +#define QT3DS_RENDER_EFFECT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "Qt3DSRenderNode.h" +#include "EASTL/string.h" +#include "Qt3DSRenderDynamicObject.h" + +namespace qt3ds { +namespace render { + struct SLayer; + struct SEffectContext; + + // Effects are post-render effect applied to the layer. There can be more than one of + // them and they have completely variable properties. + // see IEffectManager in order to create these effects. + // The data for the effect immediately follows the effect + struct SEffect : public SDynamicObject + { + private: + // These objects are only created via the dynamic object system. + SEffect(const SEffect &); + SEffect &operator=(const SEffect &); + SEffect(); + + public: + SLayer *m_Layer; + SEffect *m_NextEffect; + // Opaque pointer to context type implemented by the effect system. + // May be null in which case the effect system will generate a new context + // the first time it needs to render this effect. + SEffectContext *m_Context; + + void Initialize(); + + // If our active flag value changes, then we ask the effect manager + // to reset our context. + void SetActive(bool inActive, IEffectSystem &inSystem); + + void Reset(IEffectSystem &inSystem); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SDynamicObject::Remap(inRemapper); + inRemapper.Remap(m_Layer); + inRemapper.Remap(m_NextEffect); + inRemapper.NullPtr(m_Context); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderGraphObject.h b/src/runtimerender/graphobjects/Qt3DSRenderGraphObject.h new file mode 100644 index 0000000..58c48ed --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderGraphObject.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_GRAPH_OBJECT_H +#define QT3DS_RENDER_GRAPH_OBJECT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderTaggedPointer.h" +#include "Qt3DSRenderGraphObjectTypes.h" + +namespace qt3ds { +namespace render { + + // Types should be setup on construction. Change the type + // at your own risk as the type is used for RTTI purposes. + struct QT3DS_AUTOTEST_EXPORT SGraphObject + { + // Id's help debugging the object and are optionally set + CRegisteredString m_Id; + // Type is used for RTTI purposes down the road. + GraphObjectTypes::Enum m_Type; + STaggedPointer m_UserData; + + SGraphObject(GraphObjectTypes::Enum inType) + : m_Type(inType) + { + } + SGraphObject(const SGraphObject &inCloningObject, NVAllocatorCallback & /*inAllocator*/) + : m_Id(inCloningObject.m_Id) + , m_Type(inCloningObject.m_Type) + { + } + + // If you change any detail of the scene graph, or even *breath* on a + // scene graph object, you need to bump this binary version so at least + // we know if we can load a file or not. + static QT3DSU32 GetSceneGraphBinaryVersion() { return 1; } + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + inRemapper.Remap(m_Id); + inRemapper.NullPtr(m_UserData.m_UserData); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp b/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp new file mode 100644 index 0000000..57e86ad --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderImage.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderPluginGraphObject.h" + +using namespace qt3ds::render; + +SImage::SImage() + : SGraphObject(GraphObjectTypes::Image) + , m_RenderPlugin(nullptr) + , m_LastFrameOffscreenRenderer(nullptr) + , m_Parent(nullptr) + , m_Scale(1, 1) + , m_Pivot(0, 0) + , m_Rotation(0) + , m_Position(0, 0) + , m_MappingMode(ImageMappingModes::Normal) + , m_HorizontalTilingMode(NVRenderTextureCoordOp::ClampToEdge) + , m_VerticalTilingMode(NVRenderTextureCoordOp::ClampToEdge) +{ + m_Flags.SetActive(true); + m_Flags.SetDirty(true); + m_Flags.SetTransformDirty(true); +} + +static void HandleOffscreenResult(SImage &theImage, SImageTextureData &newImage, + SOffscreenRenderResult &theResult, bool &replaceTexture, + bool &wasDirty) +{ + newImage.m_Texture = theResult.m_Texture; + newImage.m_TextureFlags.SetHasTransparency(theResult.m_HasTransparency); + newImage.m_TextureFlags.SetPreMultiplied(true); + wasDirty = wasDirty || theResult.m_HasChangedSinceLastFrame; + theImage.m_LastFrameOffscreenRenderer = theResult.m_Renderer; + replaceTexture = true; +} + +bool SImage::ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager &inRenderManager, + IRenderPluginManager &inPluginManager, bool forIbl) +{ + + bool wasDirty = m_Flags.IsDirty(); + m_Flags.SetDirty(false); + SImageTextureData newImage; + bool replaceTexture(false); + if (m_RenderPlugin && m_RenderPlugin->m_Flags.IsActive()) { + IRenderPluginInstance *theInstance = inPluginManager.GetOrCreateRenderPluginInstance( + m_RenderPlugin->m_PluginPath, m_RenderPlugin); + if (theInstance) { + inRenderManager.MaybeRegisterOffscreenRenderer(theInstance, *theInstance); + SOffscreenRenderResult theResult = inRenderManager.GetRenderedItem(theInstance); + HandleOffscreenResult(*this, newImage, theResult, replaceTexture, wasDirty); + } + } + + if (newImage.m_Texture == nullptr) { + if (m_OffscreenRendererId.IsValid()) { + SOffscreenRenderResult theResult = + inRenderManager.GetRenderedItem(m_OffscreenRendererId); + HandleOffscreenResult(*this, newImage, theResult, replaceTexture, wasDirty); + } + } + + if (newImage.m_Texture == nullptr) { + m_LastFrameOffscreenRenderer = nullptr; + if (m_ImagePath.IsValid()) { + if (!m_LoadedTextureData + || m_LoadedTextureData->m_path != QString::fromUtf8(m_ImagePath.c_str())) { + if (m_LoadedTextureData) + m_LoadedTextureData->m_callbacks.removeOne(this); + forIbl = forIbl || m_MappingMode == ImageMappingModes::LightProbe; + m_LoadedTextureData = inBufferManager.CreateReloadableImage(m_ImagePath, false, + forIbl); + m_LoadedTextureData->m_callbacks.push_back(this); + } + if (m_LoadedTextureData) { + if (m_LoadedTextureData->m_loaded) { + newImage.m_Texture = m_LoadedTextureData->m_Texture; + newImage.m_TextureFlags = m_LoadedTextureData->m_TextureFlags; + newImage.m_BSDFMipMap = m_LoadedTextureData->m_BSDFMipMap; + } + replaceTexture = m_TextureData.m_Texture != newImage.m_Texture; + } + } + } + + if (replaceTexture) { + wasDirty = true; + m_TextureData = newImage; + } + + if (m_Flags.IsTransformDirty()) { + wasDirty = true; + CalculateTextureTransform(); + } + return wasDirty; +} + +void SImage::CalculateTextureTransform() +{ + m_Flags.SetTransformDirty(false); + + m_TextureTransform = QT3DSMat44::createIdentity(); + + QT3DSMat44 translation(QT3DSMat44::createIdentity()); + QT3DSMat44 rotation(QT3DSMat44::createIdentity()); + QT3DSMat44 scale(QT3DSMat44::createIdentity()); + + translation.column3[0] = m_Position.x; + translation.column3[1] = m_Position.y; + scale.column0[0] = m_Scale.x; + scale.column1[1] = m_Scale.y; + rotation.rotate(m_Rotation, QT3DSVec3(0, 0, 1)); + + // Setup the pivot. + m_TextureTransform.column3[0] = m_Pivot.x; + m_TextureTransform.column3[1] = m_Pivot.y; + m_TextureTransform = m_TextureTransform * rotation; + m_TextureTransform = m_TextureTransform * scale; + m_TextureTransform = m_TextureTransform * translation; +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderImage.h b/src/runtimerender/graphobjects/Qt3DSRenderImage.h new file mode 100644 index 0000000..9b71d3f --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderImage.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_IMAGE_H +#define QT3DS_RENDER_IMAGE_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "foundation/StringTable.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSVec2.h" +#include "Qt3DSRenderImageTextureData.h" +#include "EASTL/utility.h" + +namespace qt3ds { +namespace render { + class IQt3DSRenderContext; + class IOffscreenRenderManager; + class IOffscreenRenderer; + struct ImageMappingModes + { + enum Enum { + Normal = 0, // UV mapping + Environment = 1, + LightProbe = 2, + }; + }; + + struct QT3DS_AUTOTEST_EXPORT SImage : public SGraphObject + { + // Complete path to the file; + //*not* relative to the presentation directory + CRegisteredString m_ImagePath; + CRegisteredString m_ImageShaderName; ///< for custom materials we don't generate the name + + // Presentation id. + CRegisteredString m_OffscreenRendererId; // overrides source path if available + SRenderPlugin *m_RenderPlugin; // Overrides everything if available. + IOffscreenRenderer *m_LastFrameOffscreenRenderer; + SGraphObject *m_Parent; + + SImageTextureData m_TextureData; + ReloadableTexturePtr m_LoadedTextureData; + + NodeFlags m_Flags; // only dirty, transform dirty, and active apply + + QT3DSVec2 m_Scale; + QT3DSVec2 m_Pivot; + QT3DSF32 m_Rotation; // Radians. + QT3DSVec2 m_Position; + ImageMappingModes::Enum m_MappingMode; + NVRenderTextureCoordOp::Enum m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum m_VerticalTilingMode; + + // Setting any of the above variables means this object is dirty. + // Setting any of the vec2 properties means this object's transform is dirty + + QT3DSMat44 m_TextureTransform; + + SImage(); + // Renders the sub presentation + // Or finds the image. + // and sets up the texture transform + bool ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager &inRenderManager, + IRenderPluginManager &pluginManager, bool forIbl = false); + + void CalculateTextureTransform(); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_ImagePath); + inRemapper.Remap(m_OffscreenRendererId); + // Null out objects that should be null when loading from file. + inRemapper.NullPtr(m_LastFrameOffscreenRenderer); + inRemapper.NullPtr(m_TextureData.m_Texture); + inRemapper.Remap(m_RenderPlugin); + inRemapper.Remap(m_Parent); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLayer.cpp b/src/runtimerender/graphobjects/Qt3DSRenderLayer.cpp new file mode 100644 index 0000000..36a826c --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLayer.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLayer.h" +#include "Qt3DSRenderEffect.h" + +using namespace qt3ds::render; + +SLayer::SLayer() + : SNode(GraphObjectTypes::Layer) + , m_Scene(NULL) + , m_FirstEffect(NULL) + , m_RenderPlugin(NULL) + , m_ProgressiveAAMode(AAModeValues::NoAA) + , m_MultisampleAAMode(AAModeValues::NoAA) + , m_Background(LayerBackground::Transparent) + , m_ClearColor(0.0f) + , m_BlendType(LayerBlendTypes::Normal) + , m_HorizontalFieldValues(HorizontalFieldValues::LeftWidth) + , m_Left(0) + , m_LeftUnits(LayerUnitTypes::Percent) + , m_Width(100.0f) + , m_WidthUnits(LayerUnitTypes::Percent) + , m_Right(0) + , m_RightUnits(LayerUnitTypes::Percent) + , m_VerticalFieldValues(VerticalFieldValues::TopHeight) + , m_Top(0) + , m_TopUnits(LayerUnitTypes::Percent) + , m_Height(100.0f) + , m_HeightUnits(LayerUnitTypes::Percent) + , m_Bottom(0) + , m_BottomUnits(LayerUnitTypes::Percent) + , m_AoStrength(0) + , m_AoDistance(5.0f) + , m_AoSoftness(50.0f) + , m_AoBias(0) + , m_AoSamplerate(2) + , m_AoDither(false) + , m_ShadowStrength(0) + , m_ShadowDist(10) + , m_ShadowSoftness(100.0f) + , m_ShadowBias(0) + , m_LightProbe(NULL) + , m_ProbeBright(100.0f) + , m_FastIbl(false) + , m_ProbeHorizon(-1.0f) + , m_ProbeFov(180.0f) + , m_LightProbe2(NULL) + , m_Probe2Fade(1.0f) + , m_Probe2Window(1.0f) + , m_Probe2Pos(0.5f) + , m_TemporalAAEnabled(false) +{ + m_Flags.SetLayerRenderToTarget(true); + m_Flags.SetLayerEnableDepthTest(true); + m_Flags.SetLayerEnableDepthPrepass(true); +} + +void SLayer::AddEffect(SEffect &inEffect) +{ + // Effects need to be rendered in reverse order as described in the file. + inEffect.m_NextEffect = m_FirstEffect; + m_FirstEffect = &inEffect; + inEffect.m_Layer = this; +} + +SEffect *SLayer::GetLastEffect() +{ + if (m_FirstEffect) { + SEffect *theEffect = m_FirstEffect; + // Empty loop intentional + for (; theEffect->m_NextEffect; theEffect = theEffect->m_NextEffect) { + } + QT3DS_ASSERT(theEffect->m_NextEffect == NULL); + return theEffect; + } + return NULL; +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLayer.h b/src/runtimerender/graphobjects/Qt3DSRenderLayer.h new file mode 100644 index 0000000..5c08e91 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLayer.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_LAYER_H +#define QT3DS_RENDER_LAYER_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderer.h" + +namespace qt3ds { +namespace render { + class IQt3DSRenderContext; + struct SPresentation; + struct SScene; + struct SEffect; + + struct AAModeValues + { + enum Enum { + NoAA = 0, + SSAA = 1, + X2 = 2, + X4 = 4, + X8 = 8 + }; + }; + + struct HorizontalFieldValues + { + enum Enum { + LeftWidth = 0, + LeftRight, + WidthRight + }; + }; + + struct VerticalFieldValues + { + enum Enum { + TopHeight = 0, + TopBottom, + HeightBottom + }; + }; + + struct LayerUnitTypes + { + enum Enum { + Percent = 0, + Pixels + }; + }; + + struct LayerBackground + { + enum Enum { + Transparent = 0, + Unspecified, + Color + }; + }; + + struct LayerBlendTypes + { + enum Enum { + Normal = 0, + Screen, + Multiply, + Add, + Subtract, + Overlay, + ColorBurn, + ColorDodge + }; + }; + + // A layer is a special node. It *always* presents its global transform + // to children as the identity. It also can optionally have a width or height + // different than the overlying context. You can think of layers as the transformation + // between a 3d scene graph and a 2D texture. + struct QT3DS_AUTOTEST_EXPORT SLayer : public SNode + { + SScene *m_Scene; + + // First effect in a list of effects. + SEffect *m_FirstEffect; + + // If a layer has a valid texture path (one that resolves to either a + // an on-disk image or a offscreen renderer), then it does not render its + // own source path. Instead, it renders the offscreen renderer. Used in this manner, + // offscreen renderer's also have the option (if they support it) to render directly to the + // render target given a specific viewport (that is also scissored if necessary). + qt3ds::foundation::CRegisteredString m_TexturePath; + + SRenderPlugin *m_RenderPlugin; // Overrides texture path if available. + + AAModeValues::Enum m_ProgressiveAAMode; + AAModeValues::Enum m_MultisampleAAMode; + LayerBackground::Enum m_Background; + QT3DSVec4 m_ClearColor; + + LayerBlendTypes::Enum m_BlendType; + + HorizontalFieldValues::Enum m_HorizontalFieldValues; + QT3DSF32 m_Left; + LayerUnitTypes::Enum m_LeftUnits; + QT3DSF32 m_Width; + LayerUnitTypes::Enum m_WidthUnits; + QT3DSF32 m_Right; + LayerUnitTypes::Enum m_RightUnits; + + VerticalFieldValues::Enum m_VerticalFieldValues; + QT3DSF32 m_Top; + LayerUnitTypes::Enum m_TopUnits; + QT3DSF32 m_Height; + LayerUnitTypes::Enum m_HeightUnits; + QT3DSF32 m_Bottom; + LayerUnitTypes::Enum m_BottomUnits; + + // Ambient occlusion + QT3DSF32 m_AoStrength; + QT3DSF32 m_AoDistance; + QT3DSF32 m_AoSoftness; + QT3DSF32 m_AoBias; + QT3DSI32 m_AoSamplerate; + bool m_AoDither; + + // Direct occlusion + QT3DSF32 m_ShadowStrength; + QT3DSF32 m_ShadowDist; + QT3DSF32 m_ShadowSoftness; + QT3DSF32 m_ShadowBias; + + // IBL + SImage *m_LightProbe; + QT3DSF32 m_ProbeBright; + bool m_FastIbl; + QT3DSF32 m_ProbeHorizon; + QT3DSF32 m_ProbeFov; + SImage *m_LightProbe2; + QT3DSF32 m_Probe2Fade; + QT3DSF32 m_Probe2Window; + QT3DSF32 m_Probe2Pos; + + bool m_TemporalAAEnabled; + + SLayer(); + + void AddEffect(SEffect &inEffect); + + SEffect *GetLastEffect(); + + LayerBlendTypes::Enum GetLayerBlend() + { + return m_BlendType; + } + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SNode::Remap(inRemapper); + inRemapper.Remap(m_Scene); + inRemapper.Remap(m_FirstEffect); + inRemapper.Remap(m_TexturePath); + inRemapper.Remap(m_RenderPlugin); + inRemapper.Remap(m_LightProbe); + inRemapper.Remap(m_LightProbe2); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLight.cpp b/src/runtimerender/graphobjects/Qt3DSRenderLight.cpp new file mode 100644 index 0000000..5c98324 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLight.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLight.h" + +using namespace qt3ds::render; + +SLight::SLight() + : SNode(GraphObjectTypes::Light) + , m_LightType(RenderLightTypes::Directional) + , m_Scope(NULL) + , m_DiffuseColor(1, 1, 1, 1) + , m_SpecularColor(1, 1, 1, 1) + , m_AmbientColor(0, 0, 0, 1) + , m_Brightness(100) + , m_LinearFade(0) + , m_ExponentialFade(0) + , m_AreaWidth(0) + , m_AreaHeight(0) + , m_CastShadow(false) + , m_ShadowBias(0.0f) + , m_ShadowFactor(5.0f) + , m_ShadowMapRes(9) + , m_ShadowMapFar(5000.0f) + , m_ShadowMapFov(90.0f) + , m_ShadowFilter(35.0f) +{ + m_Flags.SetPointLight(0); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLight.h b/src/runtimerender/graphobjects/Qt3DSRenderLight.h new file mode 100644 index 0000000..10f2b86 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLight.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_LIGHT_H +#define QT3DS_RENDER_LIGHT_H +#include "Qt3DSRenderNode.h" + +namespace qt3ds { +namespace render { + + struct RenderLightTypes + { + enum Enum { + Unknown = 0, + Directional, + Point, + Area, + }; + }; + + struct SImage; + + struct QT3DS_AUTOTEST_EXPORT SLight : public SNode + { + RenderLightTypes::Enum m_LightType; // Directional + SNode *m_Scope; + QT3DSVec4 m_DiffuseColor; // colors are 0-1 normalized + QT3DSVec4 m_SpecularColor; // colors are 0-1 normalized + QT3DSVec4 m_AmbientColor; // colors are 0-1 normalized + + // The variables below are in the same range as Studio + // Only valid if node is a point light + QT3DSF32 m_Brightness; // 0-200 + QT3DSF32 m_LinearFade; // 0-200 + QT3DSF32 m_ExponentialFade; // 0-200 + + QT3DSF32 m_AreaWidth; // 0.01-inf + QT3DSF32 m_AreaHeight; // 0.01-inf + + bool m_CastShadow; // true if this light produce shadows + QT3DSF32 m_ShadowBias; // depth shift to avoid self-shadowing artifacts + QT3DSF32 m_ShadowFactor; // Darkening factor for ESMs + QT3DSU32 m_ShadowMapRes; // Resolution of shadow map + QT3DSF32 m_ShadowMapFar; // Far clip plane for the shadow map + QT3DSF32 m_ShadowMapFov; // Field of View for the shadow map + QT3DSF32 m_ShadowFilter; // Shadow map filter step size + + // Defaults to directional light + SLight(); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SNode::Remap(inRemapper); + inRemapper.Remap(m_Scope); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.cpp b/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.cpp new file mode 100644 index 0000000..0e5e6c6 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2015 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLightmaps.h" + +using namespace qt3ds::render; + +SLightmaps::SLightmaps() + : SGraphObject(GraphObjectTypes::Lightmaps) + , m_LightmapIndirect(NULL) + , m_LightmapRadiosity(NULL) + , m_LightmapShadow(NULL) +{ +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.h b/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.h new file mode 100644 index 0000000..4480aea --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderLightmaps.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ + +#pragma once +#ifndef QT3DS_RENDER_LIGHTMAPS_H +#define QT3DS_RENDER_LIGHTMAPS_H + +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderMaterialDirty.h" + +namespace qt3ds { +namespace render { + + struct MaterialLightmapsUsage + { + enum Enum { + Dynamic = 0, + Baked, + DynamicAndBaked, + }; + }; + + struct QT3DS_AUTOTEST_EXPORT SLightmaps : public SGraphObject + { + CMaterialDirty m_Dirty; + + SImage *m_LightmapIndirect; + SImage *m_LightmapRadiosity; + SImage *m_LightmapShadow; + + SLightmaps(); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_LightmapIndirect); + inRemapper.Remap(m_LightmapRadiosity); + inRemapper.Remap(m_LightmapShadow); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderMaterialDirty.h b/src/runtimerender/graphobjects/Qt3DSRenderMaterialDirty.h new file mode 100644 index 0000000..c04c1b6 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderMaterialDirty.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_MATERIAL_DIRTY_H +#define QT3DS_RENDER_MATERIAL_DIRTY_H + +namespace qt3ds { +namespace render { + class CMaterialDirty + { + private: + bool m_Dirty; + bool m_DirtyFlagWithInFrame; + + public: + CMaterialDirty() + : m_Dirty(true) + , m_DirtyFlagWithInFrame(m_Dirty) + { + } + + void SetDirty() { m_Dirty = m_DirtyFlagWithInFrame = true; } + bool IsDirty() const { return m_Dirty || m_DirtyFlagWithInFrame; } + void ClearDirty() { m_DirtyFlagWithInFrame = m_Dirty = false; } + void UpdateDirtyForFrame() + { + m_DirtyFlagWithInFrame = m_Dirty; + m_Dirty = false; + } + }; +} +} + +#endif // QT3DS_RENDER_MATERIAL_DIRTY_H diff --git a/src/runtimerender/graphobjects/Qt3DSRenderModel.cpp b/src/runtimerender/graphobjects/Qt3DSRenderModel.cpp new file mode 100644 index 0000000..a78396c --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderModel.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderModel.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderMesh.h" + +using namespace qt3ds::render; + +SModel::SModel() + : SNode(GraphObjectTypes::Model) + , m_FirstMaterial(NULL) + , m_SkeletonRoot(-1) + , m_TessellationMode(TessModeValues::NoTess) + , m_EdgeTess(1.0) + , m_InnerTess(1.0) + , m_WireframeMode(false) + , m_ShadowCaster(true) +{ +} + +void SModel::AddMaterial(SGraphObject &inMaterial) +{ + if (m_FirstMaterial == NULL) + m_FirstMaterial = &inMaterial; + else { + SGraphObject *lastMaterial; + // empty loop intentional + for (lastMaterial = m_FirstMaterial; lastMaterial && GetNextMaterialSibling(lastMaterial); + lastMaterial = GetNextMaterialSibling(lastMaterial)) { + } + SetNextMaterialSibling(*lastMaterial, &inMaterial); + } + if (inMaterial.m_Type == GraphObjectTypes::DefaultMaterial) + static_cast<SDefaultMaterial &>(inMaterial).m_Parent = this; +} + +NVBounds3 SModel::GetModelBounds(IBufferManager &inManager) const +{ + NVBounds3 retval; + retval.setEmpty(); + SRenderMesh *theMesh = inManager.LoadMesh(m_MeshPath); + if (theMesh) { + for (QT3DSU32 idx = 0, end = theMesh->m_Subsets.size(); idx < end; ++idx) + retval.include(theMesh->m_Subsets[idx].m_Bounds); + } + return retval; +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderModel.h b/src/runtimerender/graphobjects/Qt3DSRenderModel.h new file mode 100644 index 0000000..cc5c4e2 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderModel.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_MODEL_H +#define QT3DS_RENDER_MODEL_H + +#include "Qt3DSRenderNode.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderTessModeValues.h" + +namespace qt3ds { +namespace render { + + struct SDefaultMaterial; + class IBufferManager; + + struct QT3DS_AUTOTEST_EXPORT SModel : public SNode + { + // Complete path to the file; + //*not* relative to the presentation directory + CRegisteredString m_MeshPath; + SGraphObject *m_FirstMaterial; + QT3DSI32 m_SkeletonRoot; + TessModeValues::Enum m_TessellationMode; + QT3DSF32 m_EdgeTess; + QT3DSF32 m_InnerTess; + bool m_WireframeMode; + bool m_ShadowCaster; + + SModel(); + + void AddMaterial(SGraphObject &inMaterial); + + NVBounds3 GetModelBounds(IBufferManager &inManager) const; + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SNode::Remap(inRemapper); + inRemapper.RemapMaterial(m_FirstMaterial); + inRemapper.Remap(m_MeshPath); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp b/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp new file mode 100644 index 0000000..bf47bb8 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp @@ -0,0 +1,499 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderModel.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderPathManager.h" +#include "Qt3DSRenderPath.h" + +using namespace qt3ds::render; + +SNode::SNode(GraphObjectTypes::Enum inGraphObjectType) + : SGraphObject(inGraphObjectType) + , m_Rotation(0, 0, 0) // Radians + , m_Position(0, 0, 0) + , m_Scale(1, 1, 1) + , m_Pivot(0, 0, 0) + , m_RotationOrder(EulOrdYXZs) + , m_LocalOpacity(1.0f) + , m_GlobalOpacity(1.0f) + , m_SkeletonId(-1) + , m_Parent(NULL) + , m_NextSibling(NULL) + , m_PreviousSibling(NULL) + , m_FirstChild(NULL) + , m_DFSIndex(0) +{ + m_Flags.SetDirty(true); + m_Flags.SetTransformDirty(true); + m_Flags.SetLeftHanded(true); + m_Flags.SetActive(true); + m_Flags.SetLocallyPickable(true); +} + +SNode::SNode(const SNode &inCloningObject, NVAllocatorCallback &inAllocator) + : SGraphObject(inCloningObject, inAllocator) + , m_Rotation(inCloningObject.m_Rotation) // Radians + , m_Position(inCloningObject.m_Position) + , m_Scale(inCloningObject.m_Scale) + , m_Pivot(inCloningObject.m_Pivot) + , m_RotationOrder(inCloningObject.m_RotationOrder) + , m_LocalOpacity(inCloningObject.m_LocalOpacity) + , m_LocalTransform(inCloningObject.m_LocalTransform) + , m_GlobalTransform(inCloningObject.m_GlobalTransform) + , m_GlobalOpacity(inCloningObject.m_GlobalOpacity) + , m_SkeletonId(inCloningObject.m_SkeletonId) + , m_Parent(NULL) + , m_NextSibling(NULL) + , m_PreviousSibling(NULL) + , m_FirstChild(NULL) + , m_DFSIndex(0) +{ + m_Flags.SetDirty(true); + m_Flags.SetTransformDirty(true); + m_Flags.SetLeftHanded(true); + m_Flags.SetActive(true); + m_Flags.SetLocallyPickable(true); + + // for ( SNode* theChild = m_FirstChild; theChild != NULL; theChild = theChild->m_NextSibling ) + //{ + // SNode* theClonedChild = static_cast<SNode*>( CGraphObjectFactory::CloneGraphObject( + //*theChild, inAllocator ) ); + // AddChild( *theClonedChild ); + //} +} + +// Sets this object dirty and walks down the graph setting all +// children who are not dirty to be dirty. +void SNode::MarkDirty(NodeTransformDirtyFlag::Enum inTransformDirty) +{ + if (m_Flags.IsTransformDirty() == false) + m_Flags.SetTransformDirty(inTransformDirty != NodeTransformDirtyFlag::TransformNotDirty); + if (m_Flags.IsDirty() == false) { + m_Flags.SetDirty(true); + for (SNode *child = m_FirstChild; child; child = child->m_NextSibling) + child->MarkDirty(inTransformDirty); + } +} + +// Calculate global transform and opacity +// Walks up the graph ensure all parents are not dirty so they have +// valid global transforms. + +bool SNode::CalculateGlobalVariables() +{ + bool retval = m_Flags.IsDirty(); + if (retval) { + m_Flags.SetDirty(false); + if (m_Flags.IsTransformDirty()) + CalculateLocalTransform(); + m_GlobalOpacity = m_LocalOpacity; + if (m_Parent) { + // Layer transforms do not flow down but affect the final layer's rendered + // representation. + retval = m_Parent->CalculateGlobalVariables() || retval; + if (m_Parent->m_Type != GraphObjectTypes::Layer) { + m_GlobalOpacity *= m_Parent->m_GlobalOpacity; + if (m_Flags.IsIgnoreParentTransform() == false) + m_GlobalTransform = m_Parent->m_GlobalTransform * m_LocalTransform; + else + m_GlobalTransform = m_LocalTransform; + } else + m_GlobalTransform = m_LocalTransform; + + m_Flags.SetGlobalActive(m_Flags.IsActive() && m_Parent->m_Flags.IsGloballyActive()); + m_Flags.SetGloballyPickable(m_Flags.IsLocallyPickable() + || m_Parent->m_Flags.IsGloballyPickable()); + } else { + m_GlobalTransform = m_LocalTransform; + m_Flags.SetGlobalActive(m_Flags.IsActive()); + m_Flags.SetGloballyPickable(m_Flags.IsLocallyPickable()); + } + } + // We always clear dirty in a reasonable manner but if we aren't active + // there is no reason to tell the universe if we are dirty or not. + return retval && m_Flags.IsActive(); +} + +// Create some mapping of euler angles to their axis mapping. +#define ITERATE_POSSIBLE_EULER_ANGLES \ + HANDLE_EULER_ANGLE(EulOrdXYZs, X, Y, Z) \ + HANDLE_EULER_ANGLE(EulOrdXYXs, X, Y, X) \ + HANDLE_EULER_ANGLE(EulOrdXZYs, X, Z, Y) \ + HANDLE_EULER_ANGLE(EulOrdXZXs, X, Z, X) \ + HANDLE_EULER_ANGLE(EulOrdYZXs, Y, Z, X) \ + HANDLE_EULER_ANGLE(EulOrdYZYs, Y, Z, Y) \ + HANDLE_EULER_ANGLE(EulOrdYXZs, Y, X, Z) \ + HANDLE_EULER_ANGLE(EulOrdYXYs, Y, X, Y) \ + HANDLE_EULER_ANGLE(EulOrdZXYs, Z, X, Y) \ + HANDLE_EULER_ANGLE(EulOrdZXZs, Z, X, Z) \ + HANDLE_EULER_ANGLE(EulOrdZYXs, Z, Y, X) \ + HANDLE_EULER_ANGLE(EulOrdZYZs, Z, Y, Z) \ + HANDLE_EULER_ANGLE(EulOrdZYXr, Z, Y, X) \ + HANDLE_EULER_ANGLE(EulOrdXYXr, X, Y, X) \ + HANDLE_EULER_ANGLE(EulOrdYZXr, Y, Z, X) \ + HANDLE_EULER_ANGLE(EulOrdXZXr, X, Z, X) \ + HANDLE_EULER_ANGLE(EulOrdXZYr, X, Z, Y) \ + HANDLE_EULER_ANGLE(EulOrdYZYr, Y, Z, Y) \ + HANDLE_EULER_ANGLE(EulOrdZXYr, Z, X, Y) \ + HANDLE_EULER_ANGLE(EulOrdYXYr, Y, X, Y) \ + HANDLE_EULER_ANGLE(EulOrdYXZr, Y, X, Z) \ + HANDLE_EULER_ANGLE(EulOrdZXZr, Z, X, Z) \ + HANDLE_EULER_ANGLE(EulOrdXYZr, X, Y, Z) \ + HANDLE_EULER_ANGLE(EulOrdZYZr, Z, Y, Z) + +inline EulerAngles RotationAndOrderToShoemake(QT3DSVec3 inRotation, QT3DSU32 inOrder) +{ + EulerAngles retval; + retval.w = (QT3DSF32)inOrder; + int X = 0; + int Y = 1; + int Z = 2; + + switch (inOrder) { +#define HANDLE_EULER_ANGLE(order, xIdx, yIdx, zIdx) \ + case order: \ + retval.x = -inRotation[xIdx]; \ + retval.y = -inRotation[yIdx]; \ + retval.z = -inRotation[zIdx]; \ + break; + ITERATE_POSSIBLE_EULER_ANGLES +#undef HANDLE_EULER_ANGLE + default: + QT3DS_ASSERT(false); + retval.x = inRotation[X]; + retval.y = inRotation[Y]; + retval.z = inRotation[Z]; + break; + } + return retval; +} + +QT3DSVec3 SNode::GetRotationVectorFromRotationMatrix(const QT3DSMat33 &inMatrix) const +{ + QT3DSMat44 theConvertMatrix(inMatrix, QT3DSVec3(0, 0, 0)); + if (m_Flags.IsLeftHanded()) + SNode::FlipCoordinateSystem(theConvertMatrix); + qt3ds::render::CEulerAngleConverter theConverter; + qt3ds::render::HMatrix *theHMatrix = + reinterpret_cast<qt3ds::render::HMatrix *>(theConvertMatrix.front()); + qt3ds::render::EulerAngles theAngles = theConverter.Eul_FromHMatrix(*theHMatrix, m_RotationOrder); + return GetRotationVectorFromEulerAngles(theAngles); +} + +QT3DSVec3 SNode::GetRotationVectorFromEulerAngles(const EulerAngles &inAngles) +{ + QT3DSVec3 retval(0, 0, 0); + int X = 0; + int Y = 1; + int Z = 2; + switch ((int)inAngles.w) { +#define HANDLE_EULER_ANGLE(order, xIdx, yIdx, zIdx) \ + case order: \ + retval[xIdx] = -inAngles.x; \ + retval[yIdx] = -inAngles.y; \ + retval[zIdx] = -inAngles.z; \ + break; + ITERATE_POSSIBLE_EULER_ANGLES +#undef HANDLE_EULER_ANGLE + default: + QT3DS_ASSERT(false); + retval.x = inAngles.x; + retval.y = inAngles.y; + retval.z = inAngles.z; + break; + } + + return retval; +} + +void SNode::CalculateRotationMatrix(QT3DSMat44 &outMatrix) const +{ + StaticAssert<sizeof(QT3DSMat44) == sizeof(HMatrix)>::valid_expression(); + CEulerAngleConverter theConverter; + EulerAngles theAngles(RotationAndOrderToShoemake(m_Rotation, (int)m_RotationOrder)); + HMatrix *theMatrix = reinterpret_cast<HMatrix *>(&outMatrix); + theConverter.Eul_ToHMatrix(theAngles, *theMatrix); +} + +void SNode::FlipCoordinateSystem(QT3DSMat44 &inMatrix) +{ + QT3DSF32 *writePtr(inMatrix.front()); + // rotation conversion + writePtr[0 * 4 + 2] *= -1; + writePtr[1 * 4 + 2] *= -1; + writePtr[2 * 4 + 0] *= -1; + writePtr[2 * 4 + 1] *= -1; + + // translation conversion + writePtr[3 * 4 + 2] *= -1; +} + +void SNode::CalculateLocalTransform() +{ + m_Flags.SetTransformDirty(false); + bool leftHanded = m_Flags.IsLeftHanded(); + m_LocalTransform = QT3DSMat44::createIdentity(); + m_GlobalTransform = m_LocalTransform; + QT3DSF32 *writePtr = m_LocalTransform.front(); + QT3DSVec3 theScaledPivot(-m_Pivot[0] * m_Scale[0], -m_Pivot[1] * m_Scale[1], + -m_Pivot[2] * m_Scale[2]); + m_LocalTransform.column0[0] = m_Scale[0]; + m_LocalTransform.column1[1] = m_Scale[1]; + m_LocalTransform.column2[2] = m_Scale[2]; + + writePtr[12] = theScaledPivot[0]; + writePtr[13] = theScaledPivot[1]; + if (leftHanded) + writePtr[14] = theScaledPivot[2]; + else + writePtr[14] = -theScaledPivot[2]; + + QT3DSMat44 theRotationTransform; + CalculateRotationMatrix(theRotationTransform); + // may need column conversion in here somewhere. + m_LocalTransform = theRotationTransform * m_LocalTransform; + + writePtr[12] += m_Position[0]; + writePtr[13] += m_Position[1]; + if (leftHanded) + writePtr[14] = writePtr[14] + m_Position[2]; + else + writePtr[14] = writePtr[14] - m_Position[2]; + + if (leftHanded) { + FlipCoordinateSystem(m_LocalTransform); + } +} + +void SNode::SetLocalTransformFromMatrix(QT3DSMat44 &inTransform) +{ + m_Flags.SetTransformDirty(true); + + // clear pivot + m_Pivot[0] = m_Pivot[1] = m_Pivot[2] = 0.0f; + + // set translation + m_Position[0] = inTransform[3][0]; + m_Position[1] = inTransform[3][1]; + m_Position[2] = inTransform[3][2]; + // set scale + m_Scale[0] = inTransform.column0.magnitude(); + m_Scale[1] = inTransform.column1.magnitude(); + m_Scale[2] = inTransform.column2.magnitude(); + + // make sure there is no zero value + m_Scale[0] = (m_Scale[0] == 0.0) ? 1.0f : m_Scale[0]; + m_Scale[1] = (m_Scale[1] == 0.0) ? 1.0f : m_Scale[1]; + m_Scale[2] = (m_Scale[2] == 0.0) ? 1.0f : m_Scale[2]; + + // extract rotation by first dividing through scale value + float invScaleX = 1.0f / m_Scale[0]; + float invScaleY = 1.0f / m_Scale[1]; + float invScaleZ = 1.0f / m_Scale[2]; + + inTransform[0][0] *= invScaleX; + inTransform[0][1] *= invScaleX; + inTransform[0][2] *= invScaleX; + inTransform[1][0] *= invScaleY; + inTransform[1][1] *= invScaleY; + inTransform[1][2] *= invScaleY; + inTransform[2][0] *= invScaleZ; + inTransform[2][1] *= invScaleZ; + inTransform[2][2] *= invScaleZ; + + QT3DSMat33 theRotationMatrix(inTransform.column0.getXYZ(), inTransform.column1.getXYZ(), + inTransform.column2.getXYZ()); + m_Rotation = GetRotationVectorFromRotationMatrix(theRotationMatrix); +} + +void SNode::AddChild(SNode &inChild) +{ + if (inChild.m_Parent) + inChild.m_Parent->RemoveChild(inChild); + inChild.m_Parent = this; + if (m_FirstChild == nullptr) { + m_FirstChild = &inChild; + inChild.m_NextSibling = nullptr; + inChild.m_PreviousSibling = nullptr; + } else { + SNode *lastChild = GetLastChild(); + if (lastChild) { + lastChild->m_NextSibling = &inChild; + inChild.m_PreviousSibling = lastChild; + inChild.m_NextSibling = nullptr; + } else { + QT3DS_ASSERT(false); // no last child but first child isn't null? + } + } +} + +void SNode::RemoveChild(SNode &inChild) +{ + if (inChild.m_Parent != this) { + QT3DS_ASSERT(false); + return; + } + for (SNode *child = m_FirstChild; child; child = child->m_NextSibling) { + if (child == &inChild) { + if (child->m_PreviousSibling) + child->m_PreviousSibling->m_NextSibling = child->m_NextSibling; + if (child->m_NextSibling) + child->m_NextSibling->m_PreviousSibling = child->m_PreviousSibling; + child->m_Parent = NULL; + if (m_FirstChild == child) + m_FirstChild = child->m_NextSibling; + child->m_NextSibling = NULL; + child->m_PreviousSibling = NULL; + return; + } + } + QT3DS_ASSERT(false); +} + +SNode *SNode::GetLastChild() +{ + SNode *lastChild = NULL; + // empty loop intentional + for (lastChild = m_FirstChild; lastChild && lastChild->m_NextSibling; + lastChild = lastChild->m_NextSibling) { + } + return lastChild; +} + +void SNode::RemoveFromGraph() +{ + if (m_Parent) + m_Parent->RemoveChild(*this); + + m_NextSibling = NULL; + + // Orphan all of my children. + SNode *nextSibling = NULL; + for (SNode *child = m_FirstChild; child != NULL; child = nextSibling) { + child->m_PreviousSibling = NULL; + child->m_Parent = NULL; + nextSibling = child->m_NextSibling; + child->m_NextSibling = NULL; + } +} + +NVBounds3 SNode::GetBounds(IBufferManager &inManager, IPathManager &inPathManager, + bool inIncludeChildren, IQt3DSRenderNodeFilter *inChildFilter) const +{ + NVBounds3 retval; + retval.setEmpty(); + if (inIncludeChildren) + retval = GetChildBounds(inManager, inPathManager, inChildFilter); + + if (m_Type == GraphObjectTypes::Model) + retval.include(static_cast<const SModel *>(this)->GetModelBounds(inManager)); + else if (m_Type == GraphObjectTypes::Text) + retval.include(static_cast<const SText *>(this)->GetTextBounds()); + else if (m_Type == GraphObjectTypes::Path) + retval.include(inPathManager.GetBounds(*static_cast<const SPath *>(this))); + return retval; +} + +NVBounds3 SNode::GetChildBounds(IBufferManager &inManager, IPathManager &inPathManager, + IQt3DSRenderNodeFilter *inChildFilter) const +{ + NVBounds3 retval; + retval.setEmpty(); + for (SNode *child = m_FirstChild; child != NULL; child = child->m_NextSibling) { + if (inChildFilter == NULL || inChildFilter->IncludeNode(*child)) { + NVBounds3 childBounds; + if (child->m_Flags.IsTransformDirty()) + child->CalculateLocalTransform(); + childBounds = child->GetBounds(inManager, inPathManager); + if (childBounds.isEmpty() == false) { + // Transform the bounds into our local space. + childBounds.transform(child->m_LocalTransform); + retval.include(childBounds); + } + } + } + return retval; +} + +QT3DSVec3 SNode::GetGlobalPos() const +{ + return m_GlobalTransform.getPosition(); +} + +QT3DSVec3 SNode::GetDirection() const +{ + const QT3DSF32 *dataPtr(m_GlobalTransform.front()); + QT3DSVec3 retval(dataPtr[8], dataPtr[9], dataPtr[10]); + retval.normalize(); + return retval; +} + +QT3DSVec3 SNode::GetScalingCorrectDirection() const +{ + QT3DSMat33 theDirMatrix(m_GlobalTransform.getUpper3x3().getInverse().getTranspose()); + QT3DSVec3 theOriginalDir(0, 0, -1); + QT3DSVec3 retval = theDirMatrix.transform(theOriginalDir); + retval.normalize(); + return retval; +} + +QT3DSVec3 SNode::GetGlobalPivot() const +{ + QT3DSVec3 retval(m_Position); + retval.z *= -1; + + if (m_Parent && m_Parent->m_Type != GraphObjectTypes::Layer) + return m_Parent->m_GlobalTransform.transform(retval); + + return retval; +} + +void SNode::CalculateMVPAndNormalMatrix(const QT3DSMat44 &inViewProjection, QT3DSMat44 &outMVP, + QT3DSMat33 &outNormalMatrix) const +{ + outMVP = inViewProjection * m_GlobalTransform; + CalculateNormalMatrix(outNormalMatrix); +} + +void SNode::GetMatrixUpper3x3(QT3DSMat33 &outDest, const QT3DSMat44 &inSrc) +{ + outDest.column0 = QT3DSVec3(inSrc.column0[0], inSrc.column0[1], inSrc.column0[2]); + outDest.column1 = QT3DSVec3(inSrc.column1[0], inSrc.column1[1], inSrc.column1[2]); + outDest.column2 = QT3DSVec3(inSrc.column2[0], inSrc.column2[1], inSrc.column2[2]); +} + +void SNode::CalculateNormalMatrix(QT3DSMat33 &outNormalMatrix) const +{ + GetMatrixUpper3x3(outNormalMatrix, m_GlobalTransform); + outNormalMatrix = outNormalMatrix.getInverse().getTranspose(); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderNode.h b/src/runtimerender/graphobjects/Qt3DSRenderNode.h new file mode 100644 index 0000000..87a1500 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderNode.h @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_NODE_H +#define QT3DS_RENDER_NODE_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "foundation/Qt3DSMat44.h" +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DSBounds3.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/Qt3DSNoCopy.h" +#include "Qt3DSRenderEulerAngles.h" +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace render { + + struct SModel; + struct SLight; + struct SCamera; + struct SText; + struct SNode; + class IBufferManager; + + class INodeQueue + { + protected: + virtual ~INodeQueue() {} + public: + virtual void Enqueue(SModel &inModel) = 0; + virtual void Enqueue(SLight &inLight) = 0; + virtual void Enqueue(SCamera &inCamera) = 0; + // virtual void Enqueue( SText& inText ) = 0; + }; + + struct NodeFlagValues + { + enum Enum { + Dirty = 1, + TransformDirty = 1 << 1, + Active = 1 << 2, ///< Is this exact object active + LeftHanded = 1 << 3, + Orthographic = 1 << 4, + PointLight = 1 << 5, + GlobalActive = 1 << 6, ///< set based in Active and if a parent is active. + TextDirty = 1 << 7, + LocallyPickable = 1 << 8, + GloballyPickable = 1 << 9, + LayerEnableDepthTest = 1 << 10, + LayerRenderToTarget = 1 << 11, ///< Does this layer render to the normal render target, + ///or is it offscreen-only + ForceLayerOffscreen = 1 << 12, ///< Forces a layer to always use the offscreen rendering + ///mechanism. This can be usefulf or caching purposes. + IgnoreParentTransform = 1 << 13, + LayerEnableDepthPrePass = 1 << 14, ///< True when we render a depth pass before + }; + }; + + struct NodeTransformDirtyFlag + { + enum Enum { + TransformNotDirty, + TransformIsDirty, + }; + }; + struct NodeFlags : public NVFlags<NodeFlagValues::Enum, QT3DSU32> + { + NodeFlags() + : NVFlags<NodeFlagValues::Enum, QT3DSU32>((QT3DSU32)0) + { + } + void ClearOrSet(bool value, NodeFlagValues::Enum enumVal) { clearOrSet(value, enumVal); } + void SetActive(bool value) { ClearOrSet(value, NodeFlagValues::Active); } + bool IsActive() const { return this->operator&(NodeFlagValues::Active); } + + void SetGlobalActive(bool value) { ClearOrSet(value, NodeFlagValues::GlobalActive); } + bool IsGloballyActive() const { return this->operator&(NodeFlagValues::GlobalActive); } + + void SetTransformDirty(bool value) { ClearOrSet(value, NodeFlagValues::TransformDirty); } + bool IsTransformDirty() const { return this->operator&(NodeFlagValues::TransformDirty); } + + void SetDirty(bool value) { ClearOrSet(value, NodeFlagValues::Dirty); } + bool IsDirty() const { return this->operator&(NodeFlagValues::Dirty); } + + bool IsLeftHanded() const { return this->operator&(NodeFlagValues::LeftHanded); } + void SetLeftHanded(bool value) { ClearOrSet(value, NodeFlagValues::LeftHanded); } + + bool IsOrthographic() const { return this->operator&(NodeFlagValues::Orthographic); } + void SetOrthographic(bool value) { ClearOrSet(value, NodeFlagValues::Orthographic); } + + bool IsPointLight() const { return this->operator&(NodeFlagValues::PointLight); } + void SetPointLight(bool value) { ClearOrSet(value, NodeFlagValues::PointLight); } + + bool IsTextDirty() const { return this->operator&(NodeFlagValues::TextDirty); } + void SetTextDirty(bool value) { ClearOrSet(value, NodeFlagValues::TextDirty); } + + bool IsLocallyPickable() const { return this->operator&(NodeFlagValues::LocallyPickable); } + void SetLocallyPickable(bool value) { ClearOrSet(value, NodeFlagValues::LocallyPickable); } + + bool IsGloballyPickable() const + { + return this->operator&(NodeFlagValues::GloballyPickable); + } + void SetGloballyPickable(bool value) + { + ClearOrSet(value, NodeFlagValues::GloballyPickable); + } + + bool IsLayerRenderToTarget() const + { + return this->operator&(NodeFlagValues::LayerRenderToTarget); + } + void SetLayerRenderToTarget(bool value) + { + ClearOrSet(value, NodeFlagValues::LayerRenderToTarget); + } + + bool IsLayerEnableDepthTest() const + { + return this->operator&(NodeFlagValues::LayerEnableDepthTest); + } + void SetLayerEnableDepthTest(bool value) + { + ClearOrSet(value, NodeFlagValues::LayerEnableDepthTest); + } + + bool IsForceLayerOffscreen() const + { + return this->operator&(NodeFlagValues::ForceLayerOffscreen); + } + void SetForceLayerOffscreen(bool value) + { + ClearOrSet(value, NodeFlagValues::ForceLayerOffscreen); + } + + bool IsIgnoreParentTransform() const + { + return this->operator&(NodeFlagValues::IgnoreParentTransform); + } + void SetIgnoreParentTransform(bool value) + { + ClearOrSet(value, NodeFlagValues::IgnoreParentTransform); + } + + bool IsLayerEnableDepthPrepass() const + { + return this->operator&(NodeFlagValues::LayerEnableDepthPrePass); + } + void SetLayerEnableDepthPrepass(bool value) + { + ClearOrSet(value, NodeFlagValues::LayerEnableDepthPrePass); + } + }; + + struct QT3DS_AUTOTEST_EXPORT SNode : public SGraphObject + { + // changing any one of these means you have to + // set this object dirty + QT3DSVec3 m_Rotation; // Radians + QT3DSVec3 m_Position; + QT3DSVec3 m_Scale; + QT3DSVec3 m_Pivot; + QT3DSU32 m_RotationOrder; // UICEulerOrder::EulOrd, defaults YXZs + + // This only sets dirty, not transform dirty + // Opacity of 1 means opaque, opacity of zero means transparent. + QT3DSF32 m_LocalOpacity; + + // results of clearing dirty. + NodeFlags m_Flags; + // These end up right handed + QT3DSMat44 m_LocalTransform; + QT3DSMat44 m_GlobalTransform; + QT3DSF32 m_GlobalOpacity; + QT3DSI32 m_SkeletonId; + + // node graph members. + SNode *m_Parent; + SNode *m_NextSibling; + SNode *m_PreviousSibling; + SNode *m_FirstChild; + // Property maintained solely by the render system. + // Depth-first-search index assigned and maintained by render system. + QT3DSU32 m_DFSIndex; + + SNode(GraphObjectTypes::Enum inType = GraphObjectTypes::Node); + SNode(const SNode &inCloningObject, NVAllocatorCallback &inAllocator); + ~SNode() {} + + // Sets this object dirty and walks down the graph setting all + // children who are not dirty to be dirty. + void MarkDirty(NodeTransformDirtyFlag::Enum inTransformDirty = + NodeTransformDirtyFlag::TransformNotDirty); + + void AddChild(SNode &inChild); + void RemoveChild(SNode &inChild); + SNode *GetLastChild(); + + // Remove this node from the graph. + // It is no longer the the parent's child lists + // and all of its children no longer have a parent + // finally they are no longer siblings of each other. + void RemoveFromGraph(); + + // Calculate global transform and opacity + // Walks up the graph ensure all parents are not dirty so they have + // valid global transforms. + bool CalculateGlobalVariables(); + + // Given our rotation order and handedness, calculate the final rotation matrix + // Only the upper 3x3 of this matrix is filled in. + // If this object is left handed, then you need to call FlipCoordinateSystem + // to get a result identical to the result produced in CalculateLocalTransform + void CalculateRotationMatrix(QT3DSMat44 &outMatrix) const; + + // Get a rotation vector that would produce the given 3x.3 matrix. + // Takes m_RotationOrder and m_Flags.IsLeftHandled into account. + // Returns a rotation vector in radians. + QT3DSVec3 GetRotationVectorFromRotationMatrix(const QT3DSMat33 &inMatrix) const; + + static QT3DSVec3 GetRotationVectorFromEulerAngles(const EulerAngles &inAngles); + + // Flip a matrix from left-handed to right-handed and vice versa + static void FlipCoordinateSystem(QT3DSMat44 &ioMatrix); + + // Force the calculation of the local transform + void CalculateLocalTransform(); + + /** + * @brief setup local tranform from a matrix. + * This function decomposes a SRT matrix. + * This will fail if this matrix contains non-affine transformations + * + * @param inTransform[in] input transformation + * + * @return true backend type + */ + void SetLocalTransformFromMatrix(QT3DSMat44 &inTransform); + + // Get the bounds of us and our children in our local space. + NVBounds3 GetBounds(IBufferManager &inManager, IPathManager &inPathManager, + bool inIncludeChildren = true, + IQt3DSRenderNodeFilter *inChildFilter = NULL) const; + NVBounds3 GetChildBounds(IBufferManager &inManager, IPathManager &inPathManager, + IQt3DSRenderNodeFilter *inChildFilter = NULL) const; + // Assumes CalculateGlobalVariables has already been called. + QT3DSVec3 GetGlobalPos() const; + QT3DSVec3 GetGlobalPivot() const; + // Pulls the 3rd column out of the global transform. + QT3DSVec3 GetDirection() const; + // Multiplies (0,0,-1) by the inverse transpose of the upper 3x3 of the global transform. + // This is correct w/r/t to scaling and which the above getDirection is not. + QT3DSVec3 GetScalingCorrectDirection() const; + + // outMVP and outNormalMatrix are returned ready to upload to openGL, meaning they are + // row-major. + void CalculateMVPAndNormalMatrix(const QT3DSMat44 &inViewProjection, QT3DSMat44 &outMVP, + QT3DSMat33 &outNormalMatrix) const; + + // This should be in a utility file somewhere + static void GetMatrixUpper3x3(QT3DSMat33 &inDest, const QT3DSMat44 &inSrc); + void CalculateNormalMatrix(QT3DSMat33 &outNormalMatrix) const; + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_Parent); + inRemapper.Remap(m_FirstChild); + inRemapper.Remap(m_NextSibling); + inRemapper.Remap(m_PreviousSibling); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderPath.cpp b/src/runtimerender/graphobjects/Qt3DSRenderPath.cpp new file mode 100644 index 0000000..bc63768 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderPath.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathSubPath.h" + +using namespace qt3ds::render; + +void SPath::AddSubPath(SPathSubPath &inSegment) +{ + SPathSubPath *lastSegment = NULL; + inSegment.m_Path = this; + inSegment.m_NextSubPath = NULL; + if (m_FirstSubPath) { + // find last segment + for (lastSegment = m_FirstSubPath; lastSegment && lastSegment->m_NextSubPath; + lastSegment = lastSegment->m_NextSubPath) + ; + lastSegment->m_NextSubPath = &inSegment; + } else + m_FirstSubPath = &inSegment; +} + +void SPath::ClearSubPaths() +{ + SPathSubPath *nextSegment = NULL; + for (SPathSubPath *theSegment = m_FirstSubPath; theSegment; theSegment = nextSegment) { + nextSegment = theSegment->m_NextSubPath; + theSegment->m_Path = NULL; + theSegment->m_NextSubPath = NULL; + } + m_FirstSubPath = NULL; +}
\ No newline at end of file diff --git a/src/runtimerender/graphobjects/Qt3DSRenderPath.h b/src/runtimerender/graphobjects/Qt3DSRenderPath.h new file mode 100644 index 0000000..7db3ef5 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderPath.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PATH_H +#define QT3DS_RENDER_PATH_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderNode.h" + +namespace qt3ds { +namespace render { + struct PathCapping + { + enum Enum { + Noner = 0, + Taper = 1, + }; + }; + + struct PathTypes + { + enum Enum { + Noner = 0, + Painted, + Geometry, + }; + }; + + struct PathPaintStyles + { + enum Enum { + Noner = 0, + FilledAndStroked, + Filled, + Stroked, + }; + }; + + struct SPath : public SNode + { + PathTypes::Enum m_PathType; + QT3DSF32 m_Width; + QT3DSF32 m_LinearError; + QT3DSF32 m_EdgeTessAmount; + QT3DSF32 m_InnerTessAmount; + PathCapping::Enum m_BeginCapping; + QT3DSF32 m_BeginCapOffset; + QT3DSF32 m_BeginCapOpacity; + QT3DSF32 m_BeginCapWidth; + PathCapping::Enum m_EndCapping; + QT3DSF32 m_EndCapOffset; + QT3DSF32 m_EndCapOpacity; + QT3DSF32 m_EndCapWidth; + SGraphObject *m_Material; + SGraphObject *m_SecondMaterial; + // Paths can either be immediate - children attached define path + // or they can link to a path buffer that defines the path. + SPathSubPath *m_FirstSubPath; + CRegisteredString m_PathBuffer; + PathPaintStyles::Enum m_PaintStyle; + + bool m_WireframeMode; + // Loaded onto the card just as data. + SPath() + : SNode(GraphObjectTypes::Path) + , m_PathType(PathTypes::Geometry) + , m_Width(5.0f) + , m_LinearError(100.0f) + , m_EdgeTessAmount(8.0f) + , m_InnerTessAmount(1.0f) + , m_BeginCapping(PathCapping::Noner) + , m_BeginCapOffset(10.0f) + , m_BeginCapOpacity(.2f) + , m_BeginCapWidth(0.0f) + , m_EndCapping(PathCapping::Noner) + , m_EndCapOffset(10.0f) + , m_EndCapOpacity(.2f) + , m_EndCapWidth(0.0f) + , m_Material(NULL) + , m_SecondMaterial(NULL) + , m_FirstSubPath(NULL) + , m_PaintStyle(PathPaintStyles::Stroked) + , m_WireframeMode(false) + { + } + + bool IsStroked() const + { + return m_PaintStyle == PathPaintStyles::Stroked + || m_PaintStyle == PathPaintStyles::FilledAndStroked; + } + + bool IsFilled() const + { + return m_PaintStyle == PathPaintStyles::Filled + || m_PaintStyle == PathPaintStyles::FilledAndStroked; + } + + void AddMaterial(SGraphObject *inMaterial) + { + if (m_Material == NULL) + m_Material = inMaterial; + else + m_SecondMaterial = inMaterial; + } + + void ClearMaterials() + { + m_Material = NULL; + m_SecondMaterial = NULL; + } + + void AddSubPath(SPathSubPath &inSubPath); + void ClearSubPaths(); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SNode::Remap(inRemapper); + inRemapper.Remap(m_PathBuffer); + inRemapper.RemapMaterial(m_Material); + inRemapper.RemapMaterial(m_SecondMaterial); + inRemapper.Remap(m_FirstSubPath); + } + }; +} +} +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderPathSubPath.h b/src/runtimerender/graphobjects/Qt3DSRenderPathSubPath.h new file mode 100644 index 0000000..ed1f9be --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderPathSubPath.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PATH_SEGMENT_H +#define QT3DS_RENDER_PATH_SEGMENT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" + +namespace qt3ds { +namespace render { + struct SPathSubPath : public SGraphObject + { + SPath *m_Path; + SPathSubPath *m_NextSubPath; + bool m_Closed; + + SPathSubPath() + : SGraphObject(GraphObjectTypes::PathSubPath) + , m_Path(NULL) + , m_NextSubPath(NULL) + , m_Closed(false) + { + } + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_Path); + inRemapper.Remap(m_NextSubPath); + } + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderPresentation.cpp b/src/runtimerender/graphobjects/Qt3DSRenderPresentation.cpp new file mode 100644 index 0000000..6403959 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderPresentation.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPresentation.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderContextCore.h" + +using namespace qt3ds::render; + +void SPresentation::Render(IQt3DSRenderContext &inContext) +{ + if (m_Scene) { + NVRenderRect theViewportSize(inContext.GetRenderContext().GetViewport()); + m_Scene->Render(QT3DSVec2((QT3DSF32)theViewportSize.m_Width, (QT3DSF32)theViewportSize.m_Height), + inContext); + } +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderPresentation.h b/src/runtimerender/graphobjects/Qt3DSRenderPresentation.h new file mode 100644 index 0000000..2403e3b --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderPresentation.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PRESENTATION_H +#define QT3DS_RENDER_PRESENTATION_H + +#include "foundation/StringTable.h" +#include "Qt3DSRenderGraphObject.h" +#include "Qt3DSRenderScene.h" +#include "foundation/Qt3DSVec2.h" + +namespace qt3ds { +namespace render { + + struct RenderRotationValues + { + enum Enum { + NoRotation = 0, + Clockwise90, + Clockwise180, + Clockwise270, + }; + }; + + struct SPresentation : public SGraphObject + { + QT3DSVec2 m_PresentationDimensions; + RenderRotationValues::Enum m_PresentationRotation; + bool m_preferKTX; + SScene *m_Scene; + + CRegisteredString m_PresentationDirectory; + + SPresentation() + : SGraphObject(GraphObjectTypes::Presentation) + , m_PresentationDimensions(800, 400) + , m_PresentationRotation(RenderRotationValues::NoRotation) + , m_preferKTX(false) + , m_Scene(NULL) + { + } + + SPresentation(QT3DSF32 w, QT3DSF32 h, bool preferKTX, CRegisteredString presDir) + : SGraphObject(GraphObjectTypes::Presentation) + , m_PresentationDimensions(w, h) + , m_PresentationRotation(RenderRotationValues::NoRotation) + , m_preferKTX(preferKTX) + , m_Scene(NULL) + , m_PresentationDirectory(presDir) + { + } + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_Scene); + inRemapper.Remap(m_PresentationDirectory); + } + + void Render(IQt3DSRenderContext &inContext); + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderReferencedMaterial.h b/src/runtimerender/graphobjects/Qt3DSRenderReferencedMaterial.h new file mode 100644 index 0000000..d2043f0 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderReferencedMaterial.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_REFERENCED_MATERIAL_H +#define QT3DS_RENDER_REFERENCED_MATERIAL_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderGraphObject.h" +#include "Qt3DSRenderMaterialDirty.h" + +namespace qt3ds { +namespace render { + + struct SReferencedMaterial : SGraphObject + { + CMaterialDirty m_Dirty; + SGraphObject *m_ReferencedMaterial; + SGraphObject *m_NextSibling; + SReferencedMaterial() + : SGraphObject(GraphObjectTypes::ReferencedMaterial) + , m_ReferencedMaterial(NULL) + , m_NextSibling(NULL) + { + } + + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.RemapMaterial(m_ReferencedMaterial); + inRemapper.RemapMaterial(m_NextSibling); + } + }; +} +} + +#endif // QT3DS_RENDER_REFERENCED_MATERIAL_H diff --git a/src/runtimerender/graphobjects/Qt3DSRenderScene.cpp b/src/runtimerender/graphobjects/Qt3DSRenderScene.cpp new file mode 100644 index 0000000..7917bd7 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderScene.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderContextCore.h" +#include "render/Qt3DSRenderContext.h" + +using namespace qt3ds::render; + +SScene::SScene() + : SGraphObject(GraphObjectTypes::Scene) + , m_Presentation(NULL) + , m_FirstChild(NULL) + , m_ClearColor(0.0f) + , m_UseClearColor(true) + , m_Dirty(true) +{ +} + +void SScene::AddChild(SLayer &inLayer) +{ + if (m_FirstChild == NULL) + m_FirstChild = &inLayer; + else + GetLastChild()->m_NextSibling = &inLayer; + inLayer.m_Scene = this; +} + +SLayer *SScene::GetLastChild() +{ + // empty loop intentional + SLayer *child; + for (child = m_FirstChild; child && child->m_NextSibling; + child = (SLayer *)child->m_NextSibling) { + } + + return child; +} + +bool SScene::PrepareForRender(const QT3DSVec2 &inViewportDimensions, IQt3DSRenderContext &inContext, + const SRenderInstanceId id) +{ + // We need to iterate through the layers in reverse order and ask them to render. + bool wasDirty = m_Dirty; + m_Dirty = false; + if (m_FirstChild) { + wasDirty |= + inContext.GetRenderer().PrepareLayerForRender(*m_FirstChild, inViewportDimensions, + true, id); + } + return wasDirty; +} + +void SScene::Render(const QT3DSVec2 &inViewportDimensions, IQt3DSRenderContext &inContext, + RenderClearCommand inClearColorBuffer, const SRenderInstanceId id) +{ + if ((inClearColorBuffer == SScene::ClearIsOptional && m_UseClearColor) + || inClearColorBuffer == SScene::AlwaysClear) { + QT3DSF32 clearColorAlpha + = inContext.IsInSubPresentation() && !m_UseClearColor ? 0.0f : 1.0f; + QT3DSVec4 clearColor(0.0f, 0.0f, 0.0f, clearColorAlpha); + if (m_UseClearColor) { + clearColor.x = m_ClearColor.x; + clearColor.y = m_ClearColor.y; + clearColor.z = m_ClearColor.z; + clearColor.w = m_ClearColor.w; + } + // Maybe clear and reset to previous clear color after we leave. + qt3ds::render::NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + inContext.GetRenderContext(), &NVRenderContext::GetClearColor, + &NVRenderContext::SetClearColor, clearColor); + inContext.GetRenderContext().Clear(qt3ds::render::NVRenderClearValues::Color); + } + if (m_FirstChild) { + inContext.GetRenderer().RenderLayer(*m_FirstChild, inViewportDimensions, m_UseClearColor, + m_ClearColor, true, id); + } +} +void SScene::RenderWithClear(const QT3DSVec2 &inViewportDimensions, + IQt3DSRenderContext &inContext, + RenderClearCommand inClearColorBuffer, + QT3DSVec4 inClearColor, + const SRenderInstanceId id) +{ + // If this scene is not using clear color, we set the color + // to background color from parent layer. This allows + // fully transparent subpresentations (both scene and layer(s) transparent) + // to inherit color from the layer that contains them. + if (!m_UseClearColor) { + m_ClearColor = inClearColor; + m_UseClearColor = true; + } + Render(inViewportDimensions, inContext, inClearColorBuffer, id); +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderScene.h b/src/runtimerender/graphobjects/Qt3DSRenderScene.h new file mode 100644 index 0000000..8c4d3fe --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderScene.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SCENE_H +#define QT3DS_RENDER_SCENE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec4.h" +#include "Qt3DSRenderGraphObject.h" + +namespace qt3ds { +namespace render { + struct SLayer; + struct SPresentation; + typedef void *SRenderInstanceId; + + struct SScene : public SGraphObject + { + SPresentation *m_Presentation; + SLayer *m_FirstChild; + QT3DSVec4 m_ClearColor; + bool m_UseClearColor; + bool m_Dirty; + + enum RenderClearCommand { + ClearIsOptional = 0, + DoNotClear = 1, + AlwaysClear = 2, + }; + + SScene(); + + void AddChild(SLayer &inLayer); + SLayer *GetLastChild(); + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SGraphObject::Remap(inRemapper); + inRemapper.Remap(m_Presentation); + inRemapper.Remap(m_FirstChild); + } + // returns true if any of the layers were dirty or if this object was dirty + bool PrepareForRender(const QT3DSVec2 &inViewportDimensions, IQt3DSRenderContext &inContext, + const SRenderInstanceId id = nullptr); + void Render(const QT3DSVec2 &inViewportDimensions, IQt3DSRenderContext &inContext, + RenderClearCommand command = ClearIsOptional, + const SRenderInstanceId id = nullptr); + void RenderWithClear(const QT3DSVec2 &inViewportDimensions, IQt3DSRenderContext &inContext, + RenderClearCommand inClearColorBuffer, + QT3DSVec4 inclearColor, const SRenderInstanceId id = nullptr); + }; +} +} + +#endif diff --git a/src/runtimerender/graphobjects/Qt3DSRenderText.cpp b/src/runtimerender/graphobjects/Qt3DSRenderText.cpp new file mode 100644 index 0000000..40a20c4 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderText.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderText.h" + +using namespace qt3ds::render; + +STextRenderInfo::STextRenderInfo() + : m_FontSize(24) + , m_HorizontalAlignment(TextHorizontalAlignment::Center) + , m_VerticalAlignment(TextVerticalAlignment::Middle) + , m_Leading(0) + , m_Tracking(0) + , m_DropShadow(false) + , m_DropShadowStrength(80) + , m_DropShadowOffsetX(0) + , m_DropShadowOffsetY(0) + , m_WordWrap(TextWordWrap::WrapWord) + , m_BoundingBox(QT3DSVec2(0 ,0)) + , m_Elide(TextElide::ElideNone) + , m_ScaleX(0) + , m_ScaleY(0) + , m_EnableAcceleratedFont(false) +{ +} + +STextRenderInfo::~STextRenderInfo() +{ +} + +SText::SText() + : SNode(GraphObjectTypes::Text) + , m_TextColor(1, 1, 1, 1) + , m_TextTexture(nullptr) +{ + m_Bounds.setEmpty(); +} + +NVBounds3 SText::GetTextBounds() const +{ + return m_Bounds; +} diff --git a/src/runtimerender/graphobjects/Qt3DSRenderText.h b/src/runtimerender/graphobjects/Qt3DSRenderText.h new file mode 100644 index 0000000..13f5748 --- /dev/null +++ b/src/runtimerender/graphobjects/Qt3DSRenderText.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_TEXT_H +#define QT3DS_RENDER_TEXT_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderTextTypes.h" + +namespace qt3ds { +namespace render { + + struct SText : public SNode, public STextRenderInfo + { + // Change any of these properties and you can expect + // that the text will force an expensive re-layer and render. + // For these you need to set TextDirty. + + // These properties can change every frame with no additional cost. + QT3DSVec4 m_TextColor; + // Setup and utilized by the rendering system + NVRenderTexture2D *m_TextTexture; + STextTextureDetails m_TextTextureDetails; + // used for nv path rendering + NVRenderPathFontItem *m_PathFontItem; + NVRenderPathFontSpecification *m_PathFontDetails; + + NVBounds3 m_Bounds; + + SText(); + + NVBounds3 GetTextBounds() const; + + // Generic method used during serialization + // to remap string and object pointers + template <typename TRemapperType> + void Remap(TRemapperType &inRemapper) + { + SNode::Remap(inRemapper); + inRemapper.Remap(m_Text); + inRemapper.Remap(m_Font); + inRemapper.NullPtr(m_TextTexture); + inRemapper.NullPtr(m_PathFontItem); + inRemapper.NullPtr(m_PathFontDetails); + } + }; +} +} +#endif diff --git a/src/runtimerender/linux/DynamicLibLoader.h b/src/runtimerender/linux/DynamicLibLoader.h new file mode 100644 index 0000000..cc5d077 --- /dev/null +++ b/src/runtimerender/linux/DynamicLibLoader.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_LINUX_DYNAMIC_LIB_LOADER_H +#define QT3DS_LINUX_DYNAMIC_LIB_LOADER_H +#include <dlfcn.h> +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" + +namespace qt3ds { +namespace render { + using namespace qt3ds; + using namespace qt3ds::render; + + class CLoadedDynamicLibrary + { + void *m_DLLHandle; + CLoadedDynamicLibrary(void *hdl) + : m_DLLHandle(hdl) + { + } + CLoadedDynamicLibrary(const CLoadedDynamicLibrary &); + CLoadedDynamicLibrary &operator=(const CLoadedDynamicLibrary &); + + public: + ~CLoadedDynamicLibrary() + { + if (m_DLLHandle) + { +#ifndef _INTEGRITYPLATFORM + ::dlclose(m_DLLHandle); +#endif + } + m_DLLHandle = 0; + } + void *FindFunction(const char *name) + { +#ifndef _INTEGRITYPLATFORM + return ::dlsym(m_DLLHandle, name); +#else + qWarning() << "CLoadedDynamicLibrary::FindFunction returns NULL!"; + return NULL; +#endif + } + static CLoadedDynamicLibrary *Create(const char *inFullDllPath, NVFoundationBase &fnd) + { +#ifndef _INTEGRITYPLATFORM + void *hdl = ::dlopen(inFullDllPath, RTLD_NOW); + if (hdl == 0) { + const char *error = ::dlerror(); + qCCritical(INVALID_OPERATION, "Failed to load dynamic library %s: %s", + inFullDllPath, error); + return NULL; + } + return QT3DS_NEW(fnd.getAllocator(), CLoadedDynamicLibrary)(hdl); +#else + qWarning() << "CLoadedDynamicLibrary::Create returns NULL!"; + return NULL; +#endif + } + }; +} +} + +#endif diff --git a/src/runtimerender/macos/DynamicLibLoader.h b/src/runtimerender/macos/DynamicLibLoader.h new file mode 100644 index 0000000..c968419 --- /dev/null +++ b/src/runtimerender/macos/DynamicLibLoader.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "linux/DynamicLibLoader.h" diff --git a/src/runtimerender/q3dsqmlrender.cpp b/src/runtimerender/q3dsqmlrender.cpp new file mode 100644 index 0000000..f1bdd4d --- /dev/null +++ b/src/runtimerender/q3dsqmlrender.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "q3dsqmlrender.h" +#include "q3dsqmlstreamservice.h" +#include "render/Qt3DSRenderContext.h" + +#include <QSize> +#include <QOpenGLContext> + +Q3DSQmlRender::Q3DSQmlRender(IQt3DSRenderContext &inRenderContext, const char *asset) + : m_RenderContext(inRenderContext) + , m_qmlStreamRenderer(nullptr) + , m_offscreenRenderType(inRenderContext.GetStringTable().RegisterStr(GetRendererName())) + , m_assetString(inRenderContext.GetStringTable().RegisterStr(asset)) + , m_callback(nullptr) + , mRefCount(0) +{ + +} + +Q3DSQmlRender::~Q3DSQmlRender() +{ + m_qmlStreamRenderer = + IQ3DSQmlStreamService::getQmlStreamService()->getRenderer(m_assetString.c_str()); + if (m_qmlStreamRenderer) + m_qmlStreamRenderer->uninitialize(); +} + +CRegisteredString Q3DSQmlRender::GetOffscreenRendererType() +{ + return m_offscreenRenderType; +} + +NVRenderTextureFormats::Enum convertTextureFormat(E_TEXTURE_FORMAT fmt) +{ + NVRenderTextureFormats::Enum ret; + + switch (fmt) { + case E_TEXTURE_RGBA8: + ret = NVRenderTextureFormats::RGBA8; + break; + default: + ret = NVRenderTextureFormats::Unknown; + break; + } + + return ret; +} + +SOffscreenRendererEnvironment Q3DSQmlRender::GetDesiredEnvironment(QT3DSVec2 inPresScale) +{ + QSize size(0, 0); + E_TEXTURE_FORMAT format = E_TEXTURE_UNKNOWN; + + if (!m_qmlStreamRenderer) + initializeRenderer(); + + if (m_qmlStreamRenderer) { + size = m_qmlStreamRenderer->getDesiredSize(); + format = m_qmlStreamRenderer->getDesiredFormat(); + } + return SOffscreenRendererEnvironment( + (QT3DSU32)(size.width() * inPresScale.x), (QT3DSU32)(size.height() * inPresScale.y), + convertTextureFormat(format), OffscreenRendererDepthValues::Depth24, false, + AAModeValues::NoAA); +} + +SOffscreenRenderFlags Q3DSQmlRender::NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) +{ + Q_UNUSED(inEnvironment); + Q_UNUSED(inPresentationScaleFactor); + Q_UNUSED(instanceId); + bool render = false; + if (!m_qmlStreamRenderer) + initializeRenderer(); + if (m_qmlStreamRenderer) + render = m_qmlStreamRenderer->isUpdateRequested(); + return SOffscreenRenderFlags(false, render); +} + +void Q3DSQmlRender::Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) +{ + Q_UNUSED(inEnvironment) + Q_UNUSED(inPresentationScaleFactor) + Q_UNUSED(inColorBufferNeedsClear) + Q_UNUSED(instanceId) + if (m_qmlStreamRenderer) { + inRenderContext.PushPropertySet(); + + m_qmlStreamRenderer->render(); + + inRenderContext.PopPropertySet(true); + + if (m_callback) + m_callback->onOffscreenRendererFrame(QString(m_assetString.c_str())); + } +} + +void Q3DSQmlRender::initializeRenderer() +{ + m_qmlStreamRenderer + = IQ3DSQmlStreamService::getQmlStreamService()->getRenderer(m_assetString.c_str()); + if (m_qmlStreamRenderer) { + if (!m_qmlStreamRenderer->initialize( + QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext(), + QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext()->surface())) { + m_qmlStreamRenderer = nullptr; + } else if (m_callback) { + m_callback->onOffscreenRendererInitialized(QString(m_assetString.c_str())); + } + } +} diff --git a/src/runtimerender/q3dsqmlrender.h b/src/runtimerender/q3dsqmlrender.h new file mode 100644 index 0000000..99b395d --- /dev/null +++ b/src/runtimerender/q3dsqmlrender.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QT3DS_QML_RENDER_H +#define QT3DS_QML_RENDER_H + +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderContextCore.h" + +class IQt3DS; + +using namespace qt3ds::render; + +class IQ3DSQmlStreamService; +class IQ3DSQmlStreamRenderer; + +class Q3DSQmlRender : public IOffscreenRenderer +{ +public: + Q3DSQmlRender(IQt3DSRenderContext &inRenderContext, const char *asset); + ~Q3DSQmlRender(); + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) + + CRegisteredString GetOffscreenRendererType() override; + + SOffscreenRendererEnvironment GetDesiredEnvironment(QT3DSVec2 inPresentationScaleFactor) override; + + // Returns true of this object needs to be rendered, false if this object is not dirty + SOffscreenRenderFlags NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) override; + + void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) override; + void RenderWithClear(const SOffscreenRendererEnvironment &/*inEnvironment*/, + NVRenderContext &/*inRenderContext*/, + QT3DSVec2 /*inPresentationScaleFactor*/, + SScene::RenderClearCommand /*inColorBufferNeedsClear*/, + QT3DSVec4 /*inclearColor*/, + const SRenderInstanceId /*instanceId*/) override {} + + IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId instanceId) override + { + Q_UNUSED(instanceId) + return nullptr; + } + bool Pick(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inViewportDimensions, + const SRenderInstanceId instanceId) override + { + Q_UNUSED(inMouseCoords) + Q_UNUSED(inViewportDimensions) + Q_UNUSED(instanceId) + return false; + } + void addCallback(IOffscreenRendererCallback *cb) override + { + m_callback = cb; + } + static const char *GetRendererName() { return "qml-render"; } +private: + + void initializeRenderer(); + + IQt3DSRenderContext &m_RenderContext; + IQ3DSQmlStreamRenderer *m_qmlStreamRenderer; + CRegisteredString m_offscreenRenderType; + CRegisteredString m_assetString; + IOffscreenRendererCallback *m_callback; + volatile QT3DSI32 mRefCount; +}; + +#endif diff --git a/src/runtimerender/qnx/DynamicLibLoader.h b/src/runtimerender/qnx/DynamicLibLoader.h new file mode 100644 index 0000000..c968419 --- /dev/null +++ b/src/runtimerender/qnx/DynamicLibLoader.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "linux/DynamicLibLoader.h" diff --git a/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp new file mode 100644 index 0000000..fff7e32 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderableObjects.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderCustomMaterialRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderPathManager.h" +#include "Qt3DSRenderPathRenderContext.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +using qt3ds::foundation::CRegisteredString; + +namespace qt3ds { +namespace render { + struct SRenderableImage; + struct SShaderGeneratorGeneratedShader; + struct SSubsetRenderable; + using eastl::make_pair; + using eastl::reverse; + + STextScaleAndOffset::STextScaleAndOffset(NVRenderTexture2D &inTexture, + const STextTextureDetails &inTextDetails, + const STextRenderInfo &inInfo) + : m_TextOffset(0, 0) + , m_TextScale(1, 1) + + { + NVRenderTexture2D &theTexture = inTexture; + STextureDetails theDetails(theTexture.GetTextureDetails()); + QT3DSVec2 textDimensions(inTextDetails.m_TextWidth / 2.0f, inTextDetails.m_TextHeight / 2.0f); + textDimensions.x /= inTextDetails.m_ScaleFactor.x; + textDimensions.y /= inTextDetails.m_ScaleFactor.y; + QT3DSVec2 theTextScale(textDimensions.x, textDimensions.y); + QT3DSVec2 theTextOffset(0, 0); + + // Set the offsets to use after scaling the rect coordinates. + switch (inInfo.m_HorizontalAlignment) { + case TextHorizontalAlignment::Left: + theTextOffset[0] = theTextScale[0]; + break; + case TextHorizontalAlignment::Center: + break; + case TextHorizontalAlignment::Right: + theTextOffset[0] = -theTextScale[0]; + break; + default: + break; + } + + switch (inInfo.m_VerticalAlignment) { + case TextVerticalAlignment::Top: + theTextOffset[1] = -theTextScale[1]; + break; + case TextVerticalAlignment::Middle: + break; + case TextVerticalAlignment::Bottom: + theTextOffset[1] = theTextScale[1]; + break; + default: + break; + } + m_TextScale = theTextScale; + m_TextOffset = theTextOffset; + } + + void SSubsetRenderableBase::RenderShadowMapPass(const QT3DSVec2 &inCameraVec, + const SLight *inLight, const SCamera &inCamera, + SShadowMapEntry *inShadowMapEntry) + { + NVRenderContext &context(m_Generator.GetContext()); + SRenderableDepthPrepassShader *shader = NULL; + NVRenderInputAssembler *pIA = NULL; + + /* + if ( inLight->m_LightType == RenderLightTypes::Area ) + shader = m_Generator.GetParaboloidDepthShader( m_TessellationMode ); + else if ( inLight->m_LightType == RenderLightTypes::Directional ) + shader = m_Generator.GetOrthographicDepthShader( m_TessellationMode ); + else if ( inLight->m_LightType == RenderLightTypes::Point ) + shader = m_Generator.GetCubeShadowDepthShader( m_TessellationMode ); // This + will change to include a geometry shader pass. + */ + + if (inLight->m_LightType == RenderLightTypes::Directional) + shader = m_Generator.GetOrthographicDepthShader(m_TessellationMode); + else + shader = m_Generator.GetCubeShadowDepthShader(m_TessellationMode); + + if (shader == NULL || inShadowMapEntry == NULL) + return; + + // for phong and npatch tesselleation we need the normals too + if (m_TessellationMode == TessModeValues::NoTess + || m_TessellationMode == TessModeValues::TessLinear) + pIA = m_Subset.m_InputAssemblerDepth; + else + pIA = m_Subset.m_InputAssembler; + + QT3DSMat44 theModelViewProjection = inShadowMapEntry->m_LightVP * m_GlobalTransform; + // QT3DSMat44 theModelView = inLight->m_GlobalTransform.getInverse() * m_GlobalTransform; + + context.SetActiveShader(&shader->m_Shader); + shader->m_MVP.Set(theModelViewProjection); + shader->m_CameraPosition.Set(inCamera.m_Position); + shader->m_GlobalTransform.Set(m_GlobalTransform); + shader->m_CameraProperties.Set(inCameraVec); + /* + shader->m_CameraDirection.Set( inCamera.GetDirection() ); + + shader->m_ShadowMV[0].Set( inShadowMapEntry->m_LightCubeView[0] * m_GlobalTransform ); + shader->m_ShadowMV[1].Set( inShadowMapEntry->m_LightCubeView[1] * m_GlobalTransform ); + shader->m_ShadowMV[2].Set( inShadowMapEntry->m_LightCubeView[2] * m_GlobalTransform ); + shader->m_ShadowMV[3].Set( inShadowMapEntry->m_LightCubeView[3] * m_GlobalTransform ); + shader->m_ShadowMV[4].Set( inShadowMapEntry->m_LightCubeView[4] * m_GlobalTransform ); + shader->m_ShadowMV[5].Set( inShadowMapEntry->m_LightCubeView[5] * m_GlobalTransform ); + shader->m_Projection.Set( inCamera.m_Projection ); + */ + + // tesselation + if (m_TessellationMode != TessModeValues::NoTess) { + // set uniforms we need + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // set distance range value + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // disable culling + shader->m_Tessellation.m_DisableCulling.Set(1.0); + } + + context.SetInputAssembler(pIA); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + void SSubsetRenderableBase::RenderDepthPass(const QT3DSVec2 &inCameraVec, + SRenderableImage *inDisplacementImage, + float inDisplacementAmount) + { + NVRenderContext &context(m_Generator.GetContext()); + SRenderableDepthPrepassShader *shader = NULL; + NVRenderInputAssembler *pIA = NULL; + SRenderableImage *displacementImage = inDisplacementImage; + + if (m_Subset.m_PrimitiveType != NVRenderDrawMode::Patches) + shader = m_Generator.GetDepthPrepassShader(displacementImage != NULL); + else + shader = m_Generator.GetDepthTessPrepassShader(m_TessellationMode, + displacementImage != NULL); + + if (shader == NULL) + return; + + // for phong and npatch tesselleation or displacement mapping we need the normals (and uv's) + // too + if ((m_TessellationMode == TessModeValues::NoTess + || m_TessellationMode == TessModeValues::TessLinear) + && !displacementImage) + pIA = m_Subset.m_InputAssemblerDepth; + else + pIA = m_Subset.m_InputAssembler; + + context.SetActiveShader(&shader->m_Shader); + context.SetCullingEnabled(true); + + shader->m_MVP.Set(m_ModelContext.m_ModelViewProjection); + + if (displacementImage) { + // setup image transform + const QT3DSMat44 &textureTransform = displacementImage->m_Image.m_TextureTransform; + const QT3DSF32 *dataPtr(textureTransform.front()); + QT3DSVec3 offsets(dataPtr[12], dataPtr[13], + displacementImage->m_Image.m_TextureData.m_TextureFlags.IsPreMultiplied() + ? 1.0f + : 0.0f); + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + displacementImage->m_Image.m_TextureData.m_Texture->SetTextureWrapS( + displacementImage->m_Image.m_HorizontalTilingMode); + displacementImage->m_Image.m_TextureData.m_Texture->SetTextureWrapT( + displacementImage->m_Image.m_VerticalTilingMode); + + shader->m_DisplaceAmount.Set(inDisplacementAmount); + shader->m_DisplacementProps.m_Offsets.Set(offsets); + shader->m_DisplacementProps.m_Rotations.Set(rotations); + shader->m_DisplacementProps.m_Sampler.Set( + displacementImage->m_Image.m_TextureData.m_Texture); + } + + // tesselation + if (m_TessellationMode != TessModeValues::NoTess) { + // set uniforms we need + shader->m_GlobalTransform.Set(m_GlobalTransform); + + if (m_Generator.GetLayerRenderData() && m_Generator.GetLayerRenderData()->m_Camera) + shader->m_CameraPosition.Set( + m_Generator.GetLayerRenderData()->m_Camera->GetGlobalPos()); + else if (m_Generator.GetLayerRenderData()->m_Camera) + shader->m_CameraPosition.Set(QT3DSVec3(0.0, 0.0, 1.0)); + + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // set distance range value + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // enable culling + shader->m_Tessellation.m_DisableCulling.Set(0.0); + } + + context.SetInputAssembler(pIA); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + // An interface to the shader generator that is available to the renderables + + void SSubsetRenderable::Render(const QT3DSVec2 &inCameraVec, TShaderFeatureSet inFeatureSet) + { + NVRenderContext &context(m_Generator.GetContext()); + + SShaderGeneratorGeneratedShader *shader = m_Generator.GetShader(*this, inFeatureSet); + if (shader == NULL) + return; + + context.SetActiveShader(&shader->m_Shader); + + m_Generator.GetQt3DSContext().GetDefaultMaterialShaderGenerator().SetMaterialProperties( + shader->m_Shader, m_Material, inCameraVec, m_ModelContext.m_ModelViewProjection, + m_ModelContext.m_NormalMatrix, m_ModelContext.m_Model.m_GlobalTransform, m_FirstImage, + m_Opacity, m_Generator.GetLayerGlobalRenderProperties()); + + // tesselation + if (m_Subset.m_PrimitiveType == NVRenderDrawMode::Patches) { + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // this should finally be based on some user input + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // enable culling + shader->m_Tessellation.m_DisableCulling.Set(0.0); + + if (m_Subset.m_WireframeMode) { + // we need the viewport matrix + NVRenderRect theViewport(context.GetViewport()); + QT3DSMat44 vpMatrix; + vpMatrix.column0 = QT3DSVec4((float)theViewport.m_Width / 2.0f, 0.0, 0.0, 0.0); + vpMatrix.column1 = QT3DSVec4(0.0, (float)theViewport.m_Height / 2.0f, 0.0, 0.0); + vpMatrix.column2 = QT3DSVec4(0.0, 0.0, 1.0, 0.0); + vpMatrix.column3 = + QT3DSVec4((float)theViewport.m_Width / 2.0f + (float)theViewport.m_X, + (float)theViewport.m_Height / 2.0f + (float)theViewport.m_Y, 0.0, 1.0); + + shader->m_ViewportMatrix.Set(vpMatrix); + } + } + + context.SetCullingEnabled(true); + context.SetInputAssembler(m_Subset.m_InputAssembler); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + void SSubsetRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + SRenderableImage *displacementImage = NULL; + for (SRenderableImage *theImage = m_FirstImage; + theImage != NULL && displacementImage == NULL; theImage = theImage->m_NextImage) { + if (theImage->m_MapType == ImageMapTypes::Displacement) + displacementImage = theImage; + } + SSubsetRenderableBase::RenderDepthPass(inCameraVec, displacementImage, + m_Material.m_DisplaceAmount); + } + + void STextRenderable::Render(const QT3DSVec2 &inCameraVec) + { + NVRenderContext &context(m_Generator.GetContext()); + + if (!m_Text.m_PathFontDetails) { + + STextRenderHelper theInfo = m_Generator.GetShader(*this, false); + if (theInfo.m_Shader == NULL) + return; + // All of our shaders produce premultiplied values. + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqu); + QT3DSVec4 theColor(m_Text.m_TextColor.x, m_Text.m_TextColor.y, m_Text.m_TextColor.z, + m_Text.m_GlobalOpacity); + + STextShader &shader(*theInfo.m_Shader); + shader.Render(*m_Text.m_TextTexture, *this, theColor, m_ModelViewProjection, + inCameraVec, context, theInfo.m_QuadInputAssembler, + theInfo.m_QuadInputAssembler.GetIndexCount(), m_Text.m_TextTextureDetails, + QT3DSVec3(0, 0, 0)); + } else { + QT3DS_ASSERT(context.IsPathRenderingSupported() && context.IsProgramPipelineSupported()); + + STextRenderHelper theInfo = m_Generator.GetShader(*this, true); + if (theInfo.m_Shader == NULL) + return; + + // All of our shaders produce premultiplied values. + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqu); + QT3DSVec4 theColor(m_Text.m_TextColor.x, m_Text.m_TextColor.y, m_Text.m_TextColor.z, + m_Text.m_GlobalOpacity); + STextShader &shader(*theInfo.m_Shader); + + shader.RenderPath(*m_Text.m_PathFontItem, *m_Text.m_PathFontDetails, *this, theColor, + m_ViewProjection, m_GlobalTransform, inCameraVec, context, + m_Text.m_TextTextureDetails, QT3DSVec3(0, 0, 0)); + } + } + + void STextRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + NVRenderContext &context(m_Generator.GetContext()); + STextDepthShader *theDepthShader = m_Generator.GetTextDepthShader(); + if (theDepthShader == NULL) + return; + + if (!m_Text.m_PathFontDetails) { + // we may change stencil test state + qt3ds::render::NVRenderContextScopedProperty<bool> __stencilTest( + context, &NVRenderContext::IsStencilTestEnabled, + &NVRenderContext::SetStencilTestEnabled, true); + + NVRenderShaderProgram &theShader(theDepthShader->m_Shader); + context.SetCullingEnabled(false); + context.SetActiveShader(&theShader); + theDepthShader->m_MVP.Set(m_ModelViewProjection); + theDepthShader->m_Sampler.Set(m_Text.m_TextTexture); + const STextScaleAndOffset &theScaleAndOffset(*this); + theDepthShader->m_Dimensions.Set( + QT3DSVec4(theScaleAndOffset.m_TextScale.x, theScaleAndOffset.m_TextScale.y, + theScaleAndOffset.m_TextOffset.x, theScaleAndOffset.m_TextOffset.y)); + theDepthShader->m_CameraProperties.Set(inCameraVec); + + STextureDetails theTextureDetails = m_Text.m_TextTexture->GetTextureDetails(); + const STextTextureDetails &theTextTextureDetails(m_Text.m_TextTextureDetails); + QT3DSF32 theWidthScale = + (QT3DSF32)theTextTextureDetails.m_TextWidth / (QT3DSF32)theTextureDetails.m_Width; + QT3DSF32 theHeightScale = + (QT3DSF32)theTextTextureDetails.m_TextHeight / (QT3DSF32)theTextureDetails.m_Height; + theDepthShader->m_TextDimensions.Set( + QT3DSVec3(theWidthScale, theHeightScale, theTextTextureDetails.m_FlipY ? 1.0f : 0.0f)); + context.SetInputAssembler(&theDepthShader->m_QuadInputAssembler); + context.Draw(NVRenderDrawMode::Triangles, + theDepthShader->m_QuadInputAssembler.GetIndexCount(), 0); + } else { + qt3ds::render::NVRenderBoolOp::Enum theDepthFunction = context.GetDepthFunction(); + bool isDepthEnabled = context.IsDepthTestEnabled(); + bool isStencilEnabled = context.IsStencilTestEnabled(); + bool isDepthWriteEnabled = context.IsDepthWriteEnabled(); + qt3ds::render::NVRenderStencilFunctionArgument theArg(qt3ds::render::NVRenderBoolOp::NotEqual, + 0, 0xFF); + qt3ds::render::NVRenderStencilOperationArgument theOpArg( + qt3ds::render::NVRenderStencilOp::Keep, qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Zero); + NVScopedRefCounted<NVRenderDepthStencilState> depthStencilState = + context.CreateDepthStencilState(isDepthEnabled, isDepthWriteEnabled, + theDepthFunction, false, theArg, theArg, theOpArg, + theOpArg); + + context.SetActiveShader(NULL); + context.SetCullingEnabled(false); + + context.SetDepthStencilState(depthStencilState); + + // setup transform + QT3DSMat44 offsetMatrix = QT3DSMat44::createIdentity(); + offsetMatrix.setPosition(QT3DSVec3( + m_TextOffset.x - (QT3DSF32)m_Text.m_TextTextureDetails.m_TextWidth / 2.0f, + m_TextOffset.y - (QT3DSF32)m_Text.m_TextTextureDetails.m_TextHeight / 2.0f, 0.0)); + + QT3DSMat44 pathMatrix = m_Text.m_PathFontItem->GetTransform(); + + context.SetPathProjectionMatrix(m_ViewProjection); + context.SetPathModelViewMatrix(m_GlobalTransform * offsetMatrix * pathMatrix); + + // first pass + m_Text.m_PathFontDetails->StencilFillPathInstanced(*m_Text.m_PathFontItem); + + // second pass + context.SetStencilTestEnabled(true); + m_Text.m_PathFontDetails->CoverFillPathInstanced(*m_Text.m_PathFontItem); + + context.SetStencilTestEnabled(isStencilEnabled); + context.SetDepthFunction(theDepthFunction); + } + } + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + void SDistanceFieldRenderable::Render(const QT3DSVec2 &inCameraVec) + { + m_distanceFieldText.renderText(m_text, m_mvp); + } + + void SDistanceFieldRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + m_distanceFieldText.renderTextDepth(m_text, m_mvp); + } +#endif + + void SCustomMaterialRenderable::Render(const QT3DSVec2 & /*inCameraVec*/, + const SLayerRenderData &inLayerData, + const SLayer &inLayer, NVDataRef<SLight *> inLights, + const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, + const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SCustomMaterialRenderContext theRenderContext( + inLayer, inLayerData, inLights, inCamera, m_ModelContext.m_Model, m_Subset, + m_ModelContext.m_ModelViewProjection, m_GlobalTransform, m_ModelContext.m_NormalMatrix, + m_Material, inDepthTexture, inSsaoTexture, m_ShaderDescription, m_FirstImage, + m_Opacity); + + qt3dsContext.GetCustomMaterialSystem().RenderSubset(theRenderContext, inFeatureSet); + } + + void SCustomMaterialRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec, + const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> /*inLights*/ + , + const SCamera & /*inCamera*/, + const NVRenderTexture2D * /*inDepthTexture*/) + { + + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + if (!qt3dsContext.GetCustomMaterialSystem().RenderDepthPrepass( + m_ModelContext.m_ModelViewProjection, m_Material, m_Subset)) { + SRenderableImage *displacementImage = NULL; + for (SRenderableImage *theImage = m_FirstImage; + theImage != NULL && displacementImage == NULL; theImage = theImage->m_NextImage) { + if (theImage->m_MapType == ImageMapTypes::Displacement) + displacementImage = theImage; + } + + SSubsetRenderableBase::RenderDepthPass(inCameraVec, displacementImage, + m_Material.m_DisplaceAmount); + } + } + + void SPathRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> inLights, + const SCamera &inCamera, + const NVRenderTexture2D * /*inDepthTexture*/) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SPathRenderContext theRenderContext( + inLights, inCamera, m_Path, m_ModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, false, m_IsStroke); + + qt3dsContext.GetPathManager().RenderDepthPrepass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), TShaderFeatureSet()); + } + + void SPathRenderable::Render(const QT3DSVec2 &inCameraVec, const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D * /*inDepthTexture*/ + , + const NVRenderTexture2D * /*inSsaoTexture*/ + , + TShaderFeatureSet inFeatureSet) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SPathRenderContext theRenderContext( + inLights, inCamera, m_Path, m_ModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, m_RenderableFlags.HasTransparency(), m_IsStroke); + + qt3dsContext.GetPathManager().RenderPath( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), inFeatureSet); + } + + void SPathRenderable::RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, + SShadowMapEntry *inShadowMapEntry) + { + NVConstDataRef<SLight *> theLights; + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + + QT3DSMat44 theModelViewProjection = inShadowMapEntry->m_LightVP * m_GlobalTransform; + SPathRenderContext theRenderContext( + theLights, inCamera, m_Path, theModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, false, m_IsStroke); + + if (inLight->m_LightType != RenderLightTypes::Directional) { + qt3dsContext.GetPathManager().RenderCubeFaceShadowPass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), + TShaderFeatureSet()); + } else + qt3dsContext.GetPathManager().RenderShadowMapPass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), + TShaderFeatureSet()); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h new file mode 100644 index 0000000..a369f7a --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h @@ -0,0 +1,472 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_IMPL_RENDERABLE_OBJECTS_H +#define QT3DS_RENDER_IMPL_RENDERABLE_OBJECTS_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRenderModel.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSRenderMesh.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderShaderCache.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSDistanceFieldRenderer.h" + +namespace qt3ds { +namespace render { + + struct RenderPreparationResultFlagValues + { + enum Enum { + HasTransparency = 1 << 0, + CompletelyTransparent = 1 << 1, + Dirty = 1 << 2, + Pickable = 1 << 3, + DefaultMaterialMeshSubset = 1 << 4, + Text = 1 << 5, + Custom = 1 << 6, + CustomMaterialMeshSubset = 1 << 7, + HasRefraction = 1 << 8, + Path = 1 << 9, + ShadowCaster = 1 << 10, + DistanceField = 1 << 11, + }; + }; + + struct SRenderableObjectFlags : public NVFlags<RenderPreparationResultFlagValues::Enum, QT3DSU32> + { + void ClearOrSet(bool value, RenderPreparationResultFlagValues::Enum enumVal) + { + if (value) + this->operator|=(enumVal); + else + clear(enumVal); + } + + void SetHasTransparency(bool inHasTransparency) + { + ClearOrSet(inHasTransparency, RenderPreparationResultFlagValues::HasTransparency); + } + bool HasTransparency() const + { + return this->operator&(RenderPreparationResultFlagValues::HasTransparency); + } + bool HasRefraction() const + { + return this->operator&(RenderPreparationResultFlagValues::HasRefraction); + } + void SetCompletelyTransparent(bool inTransparent) + { + ClearOrSet(inTransparent, RenderPreparationResultFlagValues::CompletelyTransparent); + } + bool IsCompletelyTransparent() const + { + return this->operator&(RenderPreparationResultFlagValues::CompletelyTransparent); + } + void SetDirty(bool inDirty) + { + ClearOrSet(inDirty, RenderPreparationResultFlagValues::Dirty); + } + bool IsDirty() const { return this->operator&(RenderPreparationResultFlagValues::Dirty); } + void SetPickable(bool inPickable) + { + ClearOrSet(inPickable, RenderPreparationResultFlagValues::Pickable); + } + bool GetPickable() const + { + return this->operator&(RenderPreparationResultFlagValues::Pickable); + } + + // Mutually exclusive values + void SetDefaultMaterialMeshSubset(bool inMeshSubset) + { + ClearOrSet(inMeshSubset, RenderPreparationResultFlagValues::DefaultMaterialMeshSubset); + } + bool IsDefaultMaterialMeshSubset() const + { + return this->operator&(RenderPreparationResultFlagValues::DefaultMaterialMeshSubset); + } + + void SetCustomMaterialMeshSubset(bool inMeshSubset) + { + ClearOrSet(inMeshSubset, RenderPreparationResultFlagValues::CustomMaterialMeshSubset); + } + bool IsCustomMaterialMeshSubset() const + { + return this->operator&(RenderPreparationResultFlagValues::CustomMaterialMeshSubset); + } + + 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); + } + bool IsCustom() const { return this->operator&(RenderPreparationResultFlagValues::Custom); } + + void SetPath(bool inPath) { ClearOrSet(inPath, RenderPreparationResultFlagValues::Path); } + bool IsPath() const { return this->operator&(RenderPreparationResultFlagValues::Path); } + + void SetShadowCaster(bool inCaster) + { + ClearOrSet(inCaster, RenderPreparationResultFlagValues::ShadowCaster); + } + bool IsShadowCaster() const + { + return this->operator&(RenderPreparationResultFlagValues::ShadowCaster); + } + }; + + struct SNodeLightEntry + { + SLight *m_Light; + QT3DSU32 m_LightIndex; + SNodeLightEntry *m_NextNode; + SNodeLightEntry() + : m_Light(NULL) + , m_NextNode(NULL) + { + } + SNodeLightEntry(SLight *inLight, QT3DSU32 inLightIndex) + : m_Light(inLight) + , m_LightIndex(inLightIndex) + , m_NextNode(NULL) + { + } + }; + + DEFINE_INVASIVE_SINGLE_LIST(NodeLightEntry); + + IMPLEMENT_INVASIVE_SINGLE_LIST(NodeLightEntry, m_NextNode); + + struct SRenderableObject; + + typedef void (*TRenderFunction)(SRenderableObject &inObject, const QT3DSVec2 &inCameraProperties); + + struct SRenderableObject + { + // Variables used for picking + const QT3DSMat44 &m_GlobalTransform; + const NVBounds3 &m_Bounds; + SRenderableObjectFlags m_RenderableFlags; + // For rough sorting for transparency and for depth + QT3DSVec3 m_WorldCenterPoint; + QT3DSF32 m_CameraDistanceSq; + TessModeValues::Enum m_TessellationMode; + bool m_ShadowCaster; + // For custom renderable objects the render function must be defined + TRenderFunction m_RenderFunction; + TNodeLightEntryList m_ScopedLights; + SRenderableObject(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + const QT3DSMat44 &inGlobalTransform, const NVBounds3 &inBounds, + TessModeValues::Enum inTessMode = TessModeValues::NoTess, + bool inShadowCaster = true, TRenderFunction inFunction = nullptr) + + : m_GlobalTransform(inGlobalTransform) + , m_Bounds(inBounds) + , m_RenderableFlags(inFlags) + , m_WorldCenterPoint(inWorldCenterPt) + , m_CameraDistanceSq(0) + , m_TessellationMode(inTessMode) + , m_ShadowCaster(inShadowCaster) + , m_RenderFunction(inFunction) + { + } + bool operator<(SRenderableObject *inOther) const + { + return m_CameraDistanceSq < inOther->m_CameraDistanceSq; + } + }; + + typedef nvvector<SRenderableObject *> TRenderableObjectList; + + // Different subsets from the same model will get the same + // model context so we can generate the MVP and normal matrix once + // and only once per subset. + struct SModelContext + { + const SModel &m_Model; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat33 m_NormalMatrix; + + SModelContext(const SModel &inModel, const QT3DSMat44 &inViewProjection) + : m_Model(inModel) + { + m_Model.CalculateMVPAndNormalMatrix(inViewProjection, m_ModelViewProjection, + m_NormalMatrix); + } + SModelContext(const SModelContext &inOther) + : m_Model(inOther.m_Model) + { + // The default copy constructor for these objects is pretty darn slow. + memCopy(&m_ModelViewProjection, &inOther.m_ModelViewProjection, + sizeof(m_ModelViewProjection)); + memCopy(&m_NormalMatrix, &inOther.m_NormalMatrix, sizeof(m_NormalMatrix)); + } + }; + + typedef nvvector<SModelContext *> TModelContextPtrList; + + class Qt3DSRendererImpl; + struct SLayerRenderData; + struct SShadowMapEntry; + + struct SSubsetRenderableBase : public SRenderableObject + { + Qt3DSRendererImpl &m_Generator; + const SModelContext &m_ModelContext; + SRenderSubset m_Subset; + QT3DSF32 m_Opacity; + + SSubsetRenderableBase(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SModelContext &modelContext, QT3DSF32 inOpacity) + + : SRenderableObject(inFlags, inWorldCenterPt, modelContext.m_Model.m_GlobalTransform, + m_Subset.m_Bounds) + , m_Generator(gen) + , m_ModelContext(modelContext) + , m_Subset(subset) + , m_Opacity(inOpacity) + { + } + void RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, SShadowMapEntry *inShadowMapEntry); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, SRenderableImage *inDisplacementImage, + float inDisplacementAmount); + }; + + /** + * A renderable that corresponds to a subset (a part of a model). + * These are created per subset per layer and are responsible for actually + * rendering this type of object. + */ + struct SSubsetRenderable : public SSubsetRenderableBase + { + const SDefaultMaterial &m_Material; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + NVConstDataRef<QT3DSMat44> m_Bones; + + SSubsetRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SDefaultMaterial &mat, const SModelContext &modelContext, + QT3DSF32 inOpacity, SRenderableImage *inFirstImage, + SShaderDefaultMaterialKey inShaderKey, + NVConstDataRef<QT3DSMat44> inBoneGlobals) + + : SSubsetRenderableBase(inFlags, inWorldCenterPt, gen, subset, modelContext, inOpacity) + , m_Material(mat) + , m_FirstImage(inFirstImage) + , m_ShaderDescription(inShaderKey) + , m_Bones(inBoneGlobals) + { + m_RenderableFlags.SetDefaultMaterialMeshSubset(true); + m_RenderableFlags.SetCustom(false); + m_RenderableFlags.SetText(false); + m_RenderableFlags.setDistanceField(false); + } + + void Render(const QT3DSVec2 &inCameraVec, TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec); + + DefaultMaterialBlendMode::Enum getBlendingMode() + { + return m_Material.m_BlendMode; + } + }; + + struct SCustomMaterialRenderable : public SSubsetRenderableBase + { + const SCustomMaterial &m_Material; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + + SCustomMaterialRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SCustomMaterial &mat, const SModelContext &modelContext, + QT3DSF32 inOpacity, SRenderableImage *inFirstImage, + SShaderDefaultMaterialKey inShaderKey) + : SSubsetRenderableBase(inFlags, inWorldCenterPt, gen, subset, modelContext, inOpacity) + , m_Material(mat) + , m_FirstImage(inFirstImage) + , m_ShaderDescription(inShaderKey) + { + m_RenderableFlags.SetCustomMaterialMeshSubset(true); + } + + void Render(const QT3DSVec2 &inCameraVec, const SLayerRenderData &inLayerData, + const SLayer &inLayer, NVDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture); + }; + + struct STextScaleAndOffset + { + QT3DSVec2 m_TextOffset; + QT3DSVec2 m_TextScale; + STextScaleAndOffset(const QT3DSVec2 &inTextOffset, const QT3DSVec2 &inTextScale) + : m_TextOffset(inTextOffset) + , m_TextScale(inTextScale) + { + } + STextScaleAndOffset(NVRenderTexture2D &inTexture, const STextTextureDetails &inTextDetails, + const STextRenderInfo &inInfo); + }; + + struct STextRenderable : public SRenderableObject, public STextScaleAndOffset + { + Qt3DSRendererImpl &m_Generator; + const SText &m_Text; + NVRenderTexture2D &m_Texture; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat44 m_ViewProjection; + + STextRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SText &inText, const NVBounds3 &inBounds, + const QT3DSMat44 &inModelViewProjection, const QT3DSMat44 &inViewProjection, + NVRenderTexture2D &inTextTexture, const QT3DSVec2 &inTextOffset, + const QT3DSVec2 &inTextScale) + : SRenderableObject(inFlags, inWorldCenterPt, inText.m_GlobalTransform, inBounds) + , STextScaleAndOffset(inTextOffset, inTextScale) + , m_Generator(gen) + , m_Text(inText) + , m_Texture(inTextTexture) + , m_ModelViewProjection(inModelViewProjection) + , m_ViewProjection(inViewProjection) + { + 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; + SText &m_text; + + SDistanceFieldRenderable(SRenderableObjectFlags flags, QT3DSVec3 worldCenterPt, + SText &text, const NVBounds3 &bounds, const QT3DSMat44 &mvp, + Q3DSDistanceFieldRenderer &distanceFieldText) + : SRenderableObject(flags, worldCenterPt, text.m_GlobalTransform, bounds) + , m_distanceFieldText(distanceFieldText) + , m_mvp(mvp) + , m_text(text) + { + m_RenderableFlags.SetDefaultMaterialMeshSubset(false); + m_RenderableFlags.SetCustom(false); + m_RenderableFlags.SetText(false); + m_RenderableFlags.setDistanceField(true); + m_distanceFieldText.checkAndBuildGlyphs(text); + } + + void Render(const QT3DSVec2 &inCameraVec); + void RenderDepthPass(const QT3DSVec2 &inCameraVec); + }; +#endif + + struct SPathRenderable : public SRenderableObject + { + Qt3DSRendererImpl &m_Generator; + SPath &m_Path; + NVBounds3 m_Bounds; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat33 m_NormalMatrix; + const SGraphObject &m_Material; + QT3DSF32 m_Opacity; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + bool m_IsStroke; + + SPathRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const QT3DSMat44 &inGlobalTransform, + NVBounds3 &inBounds, SPath &inPath, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 inNormalMat, const SGraphObject &inMaterial, QT3DSF32 inOpacity, + SShaderDefaultMaterialKey inShaderKey, bool inIsStroke) + + : SRenderableObject(inFlags, inWorldCenterPt, inGlobalTransform, m_Bounds) + , m_Generator(gen) + , m_Path(inPath) + , m_Bounds(inBounds) + , m_ModelViewProjection(inModelViewProjection) + , m_NormalMatrix(inNormalMat) + , m_Material(inMaterial) + , m_Opacity(inOpacity) + , m_FirstImage(NULL) + , m_ShaderDescription(inShaderKey) + , m_IsStroke(inIsStroke) + { + m_RenderableFlags.SetPath(true); + } + void Render(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture); + + void RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, SShadowMapEntry *inShadowMapEntry); + }; +} +} + +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp new file mode 100644 index 0000000..0b3f665 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp @@ -0,0 +1,2051 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderBufferManager.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "EASTL/sort.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderPresentation.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSRenderEffectSystem.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include <stdlib.h> + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif + +// Quick tests you can run to find performance problems + +//#define QT3DS_RENDER_DISABLE_HARDWARE_BLENDING 1 +//#define QT3DS_RENDER_DISABLE_LIGHTING 1 +//#define QT3DS_RENDER_DISABLE_TEXTURING 1 +//#define QT3DS_RENDER_DISABLE_TRANSPARENCY 1 +//#define QT3DS_RENDER_DISABLE_FRUSTUM_CULLING 1 + +// If you are fillrate bound then sorting opaque objects can help in some circumstances +//#define QT3DS_RENDER_DISABLE_OPAQUE_SORT 1 + +using qt3ds::foundation::CRegisteredString; + +namespace qt3ds { +namespace render { + + struct SRenderableImage; + struct SShaderGeneratorGeneratedShader; + struct SSubsetRenderable; + using eastl::make_pair; + using eastl::reverse; + using eastl::stable_sort; + + SEndlType Endl; + + static SRenderInstanceId combineLayerAndId(const SLayer *layer, const SRenderInstanceId id) + { + uint64_t x = (uint64_t)layer; + x += 31u * (uint64_t)id; + return (SRenderInstanceId)x; + } + + Qt3DSRendererImpl::Qt3DSRendererImpl(IQt3DSRenderContext &ctx) + : m_qt3dsContext(ctx) + , m_Context(ctx.GetRenderContext()) + , m_BufferManager(ctx.GetBufferManager()) + , m_OffscreenRenderManager(ctx.GetOffscreenRenderManager()) + , m_StringTable(IStringTable::CreateStringTable(ctx.GetAllocator())) + , m_LayerShaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LayerShaders") + , m_Shaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_Shaders") + , m_ConstantBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_ConstantBuffers") + , m_TextShader(ctx.GetAllocator()) + , m_TextPathShader(ctx.GetAllocator()) + , m_TextWidgetShader(ctx.GetAllocator()) + , m_TextOnscreenShader(ctx.GetAllocator()) +#ifdef ADVANCED_BLEND_SW_FALLBACK + , m_LayerBlendTexture(ctx.GetResourceManager()) + , m_BlendFB(NULL) +#endif + , m_InstanceRenderMap(ctx.GetAllocator(), "Qt3DSRendererImpl::m_InstanceRenderMap") + , m_LastFrameLayers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LastFrameLayers") + , mRefCount(0) + , m_LastPickResults(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LastPickResults") + , m_CurrentLayer(NULL) + , m_WidgetVertexBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetVertexBuffers") + , m_WidgetIndexBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetIndexBuffers") + , m_WidgetShaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetShaders") + , m_WidgetInputAssembler(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetInputAssembler") + , m_BoneIdNodeMap(ctx.GetAllocator(), "Qt3DSRendererImpl::m_BoneIdNodeMap") + , m_PickRenderPlugins(true) + , m_LayerCachingEnabled(true) + , m_LayerGPuProfilingEnabled(false) + { + } + Qt3DSRendererImpl::~Qt3DSRendererImpl() + { + m_LayerShaders.clear(); + for (TShaderMap::iterator iter = m_Shaders.begin(), end = m_Shaders.end(); iter != end; + ++iter) + NVDelete(m_Context->GetAllocator(), iter->second); + + m_Shaders.clear(); + m_InstanceRenderMap.clear(); + m_ConstantBuffers.clear(); + } + + void Qt3DSRendererImpl::addRef() { atomicIncrement(&mRefCount); } + + void Qt3DSRendererImpl::release() { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Context->GetAllocator()); } + + void Qt3DSRendererImpl::ChildrenUpdated(SNode &inParent) + { + if (inParent.m_Type == GraphObjectTypes::Layer) { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(static_cast<SRenderInstanceId>(&inParent)); + if (theIter == m_InstanceRenderMap.end()) { + // The layer is not in main presentation, but it might be in subpresentation + theIter = m_InstanceRenderMap.begin(); + while (theIter != m_InstanceRenderMap.end()) { + if (static_cast<SNode *>(&theIter->second.mPtr->m_Layer) == &inParent) + break; + theIter++; + } + } + if (theIter != m_InstanceRenderMap.end()) { + theIter->second->m_CamerasAndLights.clear(); + theIter->second->m_RenderableNodes.clear(); + } + } else if (inParent.m_Parent) + ChildrenUpdated(*inParent.m_Parent); + } + + QT3DSF32 Qt3DSRendererImpl::GetTextScale(const SText &inText) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inText); + if (theData) + return theData->m_TextScale; + return 1.0f; + } + + static inline SLayer *GetNextLayer(SLayer &inLayer) + { + if (inLayer.m_NextSibling && inLayer.m_NextSibling->m_Type == GraphObjectTypes::Layer) + return static_cast<SLayer *>(inLayer.m_NextSibling); + return NULL; + } + + static inline void MaybePushLayer(SLayer &inLayer, nvvector<SLayer *> &outLayerList) + { + inLayer.CalculateGlobalVariables(); + if (inLayer.m_Flags.IsGloballyActive() && inLayer.m_Flags.IsLayerRenderToTarget()) + outLayerList.push_back(&inLayer); + } + static void BuildRenderableLayers(SLayer &inLayer, nvvector<SLayer *> &renderableLayers, + bool inRenderSiblings) + { + MaybePushLayer(inLayer, renderableLayers); + if (inRenderSiblings) { + for (SLayer *theNextLayer = GetNextLayer(inLayer); theNextLayer; + theNextLayer = GetNextLayer(*theNextLayer)) + MaybePushLayer(*theNextLayer, renderableLayers); + } + } + + void Qt3DSRendererImpl::EnableLayerGpuProfiling(bool inEnabled) + { + if (m_LayerGPuProfilingEnabled != inEnabled) { + TInstanceRenderMap::iterator theIter; + for (theIter = m_InstanceRenderMap.begin(); theIter != m_InstanceRenderMap.end(); + theIter++) { + SLayerRenderData *data = theIter->second; + if (!inEnabled) + data->m_LayerProfilerGpu = nullptr; + else + data->CreateGpuProfiler(); + } + } + m_LayerGPuProfilingEnabled = inEnabled; + } + + bool Qt3DSRendererImpl::PrepareLayerForRender(SLayer &inLayer, + const QT3DSVec2 &inViewportDimensions, + bool inRenderSiblings, + const SRenderInstanceId id) + { + (void)inViewportDimensions; + nvvector<SLayer *> renderableLayers(m_qt3dsContext.GetPerFrameAllocator(), "LayerVector"); + // Found by fair roll of the dice. + renderableLayers.reserve(4); + + BuildRenderableLayers(inLayer, renderableLayers, inRenderSiblings); + + bool retval = false; + + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + // Store the previous state of if we were rendering a layer. + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + + if (theRenderData) { + theRenderData->PrepareForRender(); + retval = retval || theRenderData->m_LayerPrepResult->m_Flags.WasDirty(); + } else { + QT3DS_ASSERT(false); + } + } + + return retval; + } + + void Qt3DSRendererImpl::RenderLayer(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool clear, QT3DSVec4 clearColor, bool inRenderSiblings, + const SRenderInstanceId id) + { + (void)inViewportDimensions; + nvvector<SLayer *> renderableLayers(m_qt3dsContext.GetPerFrameAllocator(), "LayerVector"); + // Found by fair roll of the dice. + renderableLayers.reserve(4); + + BuildRenderableLayers(inLayer, renderableLayers, inRenderSiblings); + + NVRenderContext &theRenderContext(m_qt3dsContext.GetRenderContext()); + qt3ds::render::NVRenderFrameBuffer *theFB = theRenderContext.GetRenderTarget(); + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + SLayerRenderPreparationResult &prepRes(*theRenderData->m_LayerPrepResult); + LayerBlendTypes::Enum layerBlend = prepRes.GetLayer()->GetLayerBlend(); +#ifdef ADVANCED_BLEND_SW_FALLBACK + if ((layerBlend == LayerBlendTypes::Overlay || + layerBlend == LayerBlendTypes::ColorBurn || + layerBlend == LayerBlendTypes::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR()) { + // Create and set up FBO and texture for advanced blending SW fallback + NVRenderRect viewport = theRenderContext.GetViewport(); + m_LayerBlendTexture.EnsureTexture(viewport.m_Width + viewport.m_X, + viewport.m_Height + viewport.m_Y, + NVRenderTextureFormats::RGBA8); + if (m_BlendFB == NULL) + m_BlendFB = theRenderContext.CreateFrameBuffer(); + m_BlendFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerBlendTexture); + theRenderContext.SetRenderTarget(m_BlendFB); + theRenderContext.SetScissorTestEnabled(false); + QT3DSVec4 color(0.0f); + if (clear) + color = clearColor; + + QT3DSVec4 origColor = theRenderContext.GetClearColor(); + theRenderContext.SetClearColor(color); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + theRenderContext.SetClearColor(origColor); + theRenderContext.SetRenderTarget(theFB); + break; + } else { + m_LayerBlendTexture.ReleaseTexture(); + } +#endif + } + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + // Store the previous state of if we were rendering a layer. + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + + if (theRenderData) { + if (theRenderData->m_LayerPrepResult->IsLayerVisible()) + theRenderData->RunnableRenderToViewport(theFB); + } else { + QT3DS_ASSERT(false); + } + } + } + + SLayer *Qt3DSRendererImpl::GetLayerForNode(const SNode &inNode) const + { + if (inNode.m_Type == GraphObjectTypes::Layer) { + return &const_cast<SLayer &>(static_cast<const SLayer &>(inNode)); + } + if (inNode.m_Parent) + return GetLayerForNode(*inNode.m_Parent); + return NULL; + } + + SLayerRenderData *Qt3DSRendererImpl::GetOrCreateLayerRenderDataForNode(const SNode &inNode, + const SRenderInstanceId id) + { + const SLayer *theLayer = GetLayerForNode(inNode); + if (theLayer) { + TInstanceRenderMap::const_iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(theLayer, id)); + if (theIter != m_InstanceRenderMap.end()) + return const_cast<SLayerRenderData *>(theIter->second.mPtr); + + SLayerRenderData *theRenderData = QT3DS_NEW(m_Context->GetAllocator(), SLayerRenderData)( + const_cast<SLayer &>(*theLayer), *this); + m_InstanceRenderMap.insert(make_pair(combineLayerAndId(theLayer, id), theRenderData)); + + // create a profiler if enabled + if (IsLayerGpuProfilingEnabled() && theRenderData) + theRenderData->CreateGpuProfiler(); + + return theRenderData; + } + return NULL; + } + + SCamera *Qt3DSRendererImpl::GetCameraForNode(const SNode &inNode) const + { + SLayerRenderData *theLayer = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theLayer) + return theLayer->m_Camera; + return NULL; + } + + Option<SCuboidRect> Qt3DSRendererImpl::GetCameraBounds(const SGraphObject &inObject) + { + if (GraphObjectTypes::IsNodeType(inObject.m_Type)) { + const SNode &theNode = static_cast<const SNode &>(inObject); + SLayerRenderData *theLayer = GetOrCreateLayerRenderDataForNode(theNode); + if (theLayer->GetOffscreenRenderer() == false) { + SCamera *theCamera = theLayer->m_Camera; + if (theCamera) + return theCamera->GetCameraBounds( + theLayer->m_LayerPrepResult->GetLayerToPresentationViewport(), + theLayer->m_LayerPrepResult->GetPresentationDesignDimensions()); + } + } + return Option<SCuboidRect>(); + } + + void Qt3DSRendererImpl::DrawScreenRect(NVRenderRectF inRect, const QT3DSVec3 &inColor) + { + SCamera theScreenCamera; + theScreenCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + NVRenderRectF theViewport(m_Context->GetViewport()); + theScreenCamera.m_Flags.SetOrthographic(true); + theScreenCamera.CalculateGlobalVariables(theViewport, + QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + GenerateXYQuad(); + if (!m_ScreenRectShader) { + IShaderProgramGenerator &theGenerator(GetProgramGenerator()); + theGenerator.BeginProgram(); + IShaderStageGenerator &vertexGenerator( + *theGenerator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *theGenerator.GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddUniform("rectangle_dims", "vec3"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos * rectangle_dims, 1.0);"); + vertexGenerator.Append("}"); + fragmentGenerator.AddUniform("output_color", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tgl_FragColor.rgb = output_color;"); + fragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + fragmentGenerator.Append("}"); + // No flags enabled + m_ScreenRectShader = theGenerator.CompileGeneratedShader( + "DrawScreenRect", SShaderCacheProgramFlags(), TShaderFeatureSet()); + } + if (m_ScreenRectShader) { + // Fudge the rect by one pixel to ensure we see all the corners. + if (inRect.m_Width > 1) + inRect.m_Width -= 1; + if (inRect.m_Height > 1) + inRect.m_Height -= 1; + inRect.m_X += 1; + inRect.m_Y += 1; + // Figure out the rect center. + SNode theNode; + + QT3DSVec2 rectGlobalCenter = inRect.Center(); + QT3DSVec2 rectCenter(theViewport.ToNormalizedRectRelative(rectGlobalCenter)); + theNode.m_Position.x = rectCenter.x; + theNode.m_Position.y = rectCenter.y; + theNode.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theNode.CalculateGlobalVariables(); + QT3DSMat44 theViewProjection; + theScreenCamera.CalculateViewProjectionMatrix(theViewProjection); + QT3DSMat44 theMVP; + QT3DSMat33 theNormal; + theNode.CalculateMVPAndNormalMatrix(theViewProjection, theMVP, theNormal); + m_Context->SetBlendingEnabled(false); + m_Context->SetDepthWriteEnabled(false); + m_Context->SetDepthTestEnabled(false); + m_Context->SetCullingEnabled(false); + m_Context->SetActiveShader(m_ScreenRectShader); + m_ScreenRectShader->SetPropertyValue("model_view_projection", theMVP); + m_ScreenRectShader->SetPropertyValue("output_color", inColor); + m_ScreenRectShader->SetPropertyValue( + "rectangle_dims", QT3DSVec3(inRect.m_Width / 2.0f, inRect.m_Height / 2.0f, 0.0f)); + } + if (!m_RectInputAssembler) { + QT3DS_ASSERT(m_QuadVertexBuffer); + QT3DSU8 indexData[] = { 0, 1, 1, 2, 2, 3, 3, 0 }; + + m_RectIndexBuffer = m_Context->CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, + qt3ds::render::NVRenderComponentTypes::QT3DSU8, sizeof(indexData), + toConstDataRef(indexData, sizeof(indexData))); + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + }; + + // create our attribute layout + m_RectAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1)); + + QT3DSU32 strides = m_QuadVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_RectInputAssembler = m_Context->CreateInputAssembler( + m_RectAttribLayout, toConstDataRef(&m_QuadVertexBuffer.mPtr, 1), m_RectIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + m_Context->SetInputAssembler(m_RectInputAssembler); + m_Context->Draw(NVRenderDrawMode::Lines, m_RectIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::SetupWidgetLayer() + { + NVRenderContext &theContext = m_qt3dsContext.GetRenderContext(); + + if (!m_WidgetTexture) { + IResourceManager &theManager = m_qt3dsContext.GetResourceManager(); + m_WidgetTexture = theManager.AllocateTexture2D(m_BeginFrameViewport.m_Width, + m_BeginFrameViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_WidgetFBO = theManager.AllocateFrameBuffer(); + m_WidgetFBO->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*m_WidgetTexture)); + theContext.SetRenderTarget(m_WidgetFBO); + + // NVRenderRect theScissorRect( 0, 0, m_BeginFrameViewport.m_Width, + // m_BeginFrameViewport.m_Height ); + // NVRenderContextScopedProperty<NVRenderRect> __scissorRect( theContext, + // &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect, theScissorRect ); + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + m_Context->SetClearColor(QT3DSVec4(0, 0, 0, 0)); + m_Context->Clear(NVRenderClearValues::Color); + + } else + theContext.SetRenderTarget(m_WidgetFBO); + } + + void Qt3DSRendererImpl::BeginFrame() + { + for (QT3DSU32 idx = 0, end = m_LastFrameLayers.size(); idx < end; ++idx) + m_LastFrameLayers[idx]->ResetForFrame(); + m_LastFrameLayers.clear(); + m_BeginFrameViewport = m_qt3dsContext.GetRenderList().GetViewport(); + } + void Qt3DSRendererImpl::EndFrame() + { + if (m_WidgetTexture) { + using qt3ds::render::NVRenderContextScopedProperty; + // Releasing the widget FBO can set it as the active frame buffer. + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + *m_Context, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + STextureDetails theDetails = m_WidgetTexture->GetTextureDetails(); + m_Context->SetBlendingEnabled(true); + // Colors are expected to be non-premultiplied, so we premultiply alpha into them at + // this point. + m_Context->SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha)); + m_Context->SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + + m_Context->SetDepthTestEnabled(false); + m_Context->SetScissorTestEnabled(false); + m_Context->SetViewport(m_BeginFrameViewport); + SCamera theCamera; + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theTextureDims((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theDetails.m_Width, theDetails.m_Height), theTextureDims); + QT3DSMat44 theViewProj; + theCamera.CalculateViewProjectionMatrix(theViewProj); + RenderQuad(theTextureDims, theViewProj, *m_WidgetTexture); + + IResourceManager &theManager(m_qt3dsContext.GetResourceManager()); + theManager.Release(*m_WidgetFBO); + theManager.Release(*m_WidgetTexture); + m_WidgetTexture = NULL; + m_WidgetFBO = NULL; + } + } + + inline bool PickResultLessThan(const Qt3DSRenderPickResult &lhs, const Qt3DSRenderPickResult &rhs) + { + return FloatLessThan(lhs.m_CameraDistanceSq, rhs.m_CameraDistanceSq); + } + + inline QT3DSF32 ClampUVCoord(QT3DSF32 inUVCoord, NVRenderTextureCoordOp::Enum inCoordOp) + { + if (inUVCoord > 1.0f || inUVCoord < 0.0f) { + switch (inCoordOp) { + default: + QT3DS_ASSERT(false); + break; + case NVRenderTextureCoordOp::ClampToEdge: + inUVCoord = NVMin(inUVCoord, 1.0f); + inUVCoord = NVMax(inUVCoord, 0.0f); + break; + case NVRenderTextureCoordOp::Repeat: { + QT3DSF32 multiplier = inUVCoord > 0.0f ? 1.0f : -1.0f; + QT3DSF32 clamp = fabs(inUVCoord); + clamp = clamp - floor(clamp); + if (multiplier < 0) + inUVCoord = 1.0f - clamp; + else + inUVCoord = clamp; + } break; + case NVRenderTextureCoordOp::MirroredRepeat: { + QT3DSF32 multiplier = inUVCoord > 0.0f ? 1.0f : -1.0f; + QT3DSF32 clamp = fabs(inUVCoord); + if (multiplier > 0.0f) + clamp -= 1.0f; + QT3DSU32 isMirrored = ((QT3DSU32)clamp) % 2 == 0; + QT3DSF32 remainder = clamp - floor(clamp); + inUVCoord = remainder; + if (isMirrored) { + if (multiplier > 0.0f) + inUVCoord = 1.0f - inUVCoord; + } else { + if (multiplier < 0.0f) + inUVCoord = 1.0f - remainder; + } + } break; + } + } + return inUVCoord; + } + + static eastl::pair<QT3DSVec2, QT3DSVec2> + GetMouseCoordsAndViewportFromSubObject(QT3DSVec2 inLocalHitUVSpace, + Qt3DSRenderPickSubResult &inSubResult) + { + QT3DSMat44 theTextureMatrix(inSubResult.m_TextureMatrix); + QT3DSVec3 theNewUVCoords( + theTextureMatrix.transform(QT3DSVec3(inLocalHitUVSpace.x, inLocalHitUVSpace.y, 0))); + theNewUVCoords.x = ClampUVCoord(theNewUVCoords.x, inSubResult.m_HorizontalTilingMode); + theNewUVCoords.y = ClampUVCoord(theNewUVCoords.y, inSubResult.m_VerticalTilingMode); + QT3DSVec2 theViewportDimensions = + QT3DSVec2((QT3DSF32)inSubResult.m_ViewportWidth, (QT3DSF32)inSubResult.m_ViewportHeight); + QT3DSVec2 theMouseCoords(theNewUVCoords.x * theViewportDimensions.x, + (1.0f - theNewUVCoords.y) * theViewportDimensions.y); + + return eastl::make_pair(theMouseCoords, theViewportDimensions); + } + + SPickResultProcessResult Qt3DSRendererImpl::ProcessPickResultList(bool inPickEverything) + { + if (m_LastPickResults.empty()) + return SPickResultProcessResult(); + // Things are rendered in a particular order and we need to respect that ordering. + eastl::stable_sort(m_LastPickResults.begin(), m_LastPickResults.end(), PickResultLessThan); + + // We need to pick against sub objects basically somewhat recursively + // but if we don't hit any sub objects and the parent isn't pickable then + // we need to move onto the next item in the list. + // We need to keep in mind that theQuery->Pick will enter this method in a later + // stack frame so *if* we get to sub objects we need to pick against them but if the pick + // completely misses *and* the parent object locally pickable is false then we need to move + // onto the next object. + + QT3DSU32 maxPerFrameAllocationPickResultCount = + SFastAllocator<>::SlabSize / sizeof(Qt3DSRenderPickResult); + QT3DSU32 numToCopy = + NVMin(maxPerFrameAllocationPickResultCount, (QT3DSU32)m_LastPickResults.size()); + QT3DSU32 numCopyBytes = numToCopy * sizeof(Qt3DSRenderPickResult); + Qt3DSRenderPickResult *thePickResults = reinterpret_cast<Qt3DSRenderPickResult *>( + GetPerFrameAllocator().allocate(numCopyBytes, "tempPickData", __FILE__, __LINE__)); + memCopy(thePickResults, m_LastPickResults.data(), numCopyBytes); + m_LastPickResults.clear(); + bool foundValidResult = false; + SPickResultProcessResult thePickResult(thePickResults[0]); + for (size_t idx = 0; idx < numToCopy && foundValidResult == false; ++idx) { + thePickResult = thePickResults[idx]; + // Here we do a hierarchy. Picking against sub objects takes precedence. + // If picking against the sub object doesn't return a valid result *and* + // the current object isn't globally pickable then we move onto the next object returned + // by the pick query. + if (thePickResult.m_HitObject != NULL && thePickResult.m_FirstSubObject != NULL + && m_PickRenderPlugins) { + QT3DSVec2 theUVCoords(thePickResult.m_LocalUVCoords.x, + thePickResult.m_LocalUVCoords.y); + IOffscreenRenderer *theSubRenderer(thePickResult.m_FirstSubObject->m_SubRenderer); + eastl::pair<QT3DSVec2, QT3DSVec2> mouseAndViewport = + GetMouseCoordsAndViewportFromSubObject(theUVCoords, + *thePickResult.m_FirstSubObject); + QT3DSVec2 theMouseCoords = mouseAndViewport.first; + QT3DSVec2 theViewportDimensions = mouseAndViewport.second; + IGraphObjectPickQuery *theQuery = theSubRenderer->GetGraphObjectPickQuery(this); + if (theQuery) { + Qt3DSRenderPickResult theInnerPickResult = + theQuery->Pick(theMouseCoords, theViewportDimensions, inPickEverything); + if (theInnerPickResult.m_HitObject) { + thePickResult = theInnerPickResult; + thePickResult.m_OffscreenRenderer = theSubRenderer; + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } else if (GraphObjectTypes::IsNodeType(thePickResult.m_HitObject->m_Type)) { + const SNode *theNode = + static_cast<const SNode *>(thePickResult.m_HitObject); + if (theNode->m_Flags.IsGloballyPickable() == true) { + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } + } + } else { + // If the sub renderer doesn't consume the pick then we return the picked object + // itself. So no matter what, if we get to here the pick was consumed. + thePickResult.m_WasPickConsumed = true; + bool wasPickConsumed = + theSubRenderer->Pick(theMouseCoords, theViewportDimensions, this); + if (wasPickConsumed) { + thePickResult.m_HitObject = NULL; + foundValidResult = true; + } + } + } else { + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } + } + return thePickResult; + } + + Qt3DSRenderPickResult Qt3DSRendererImpl::Pick(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickSiblings, + bool inPickEverything, const SRenderInstanceId id) + { + m_LastPickResults.clear(); + + SLayer *theLayer = &inLayer; + // Stepping through how the original runtime did picking it picked layers in order + // stopping at the first hit. So objects on the top layer had first crack at the pick + // vector itself. + do { + if (theLayer->m_Flags.IsActive()) { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(theLayer, id)); + if (theIter != m_InstanceRenderMap.end()) { + m_LastPickResults.clear(); + GetLayerHitObjectList(*theIter->second, inViewportDimensions, inMouseCoords, + inPickEverything, m_LastPickResults, + GetPerFrameAllocator()); + SPickResultProcessResult retval(ProcessPickResultList(inPickEverything)); + if (retval.m_WasPickConsumed) + return retval; + } else { + // QT3DS_ASSERT( false ); + } + } + + if (inPickSiblings) + theLayer = GetNextLayer(*theLayer); + else + theLayer = NULL; + } while (theLayer != NULL); + + return Qt3DSRenderPickResult(); + } + + static inline Option<QT3DSVec2> IntersectRayWithNode(const SNode &inNode, + SRenderableObject &inRenderableObject, + const SRay &inPickRay) + { + if (inRenderableObject.m_RenderableFlags.IsText()) { + STextRenderable &theRenderable = static_cast<STextRenderable &>(inRenderableObject); + 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) + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } else if (inRenderableObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + SCustomMaterialRenderable &theRenderable = + static_cast<SCustomMaterialRenderable &>(inRenderableObject); + if (&theRenderable.m_ModelContext.m_Model == &inNode) + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } else { + QT3DS_ASSERT(false); + } + return Empty(); + } + + static inline Qt3DSRenderPickSubResult ConstructSubResult(SImage &inImage) + { + STextureDetails theDetails = inImage.m_TextureData.m_Texture->GetTextureDetails(); + return Qt3DSRenderPickSubResult(*inImage.m_LastFrameOffscreenRenderer, + inImage.m_TextureTransform, inImage.m_HorizontalTilingMode, + inImage.m_VerticalTilingMode, theDetails.m_Width, + theDetails.m_Height); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::FacePosition(SNode &inNode, NVBounds3 inBounds, + const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + NVDataRef<SGraphObject *> inMapperObjects, + SBasisPlanes::Enum inPlane) + { + SLayerRenderData *theLayerData = GetOrCreateLayerRenderDataForNode(inNode); + if (theLayerData == NULL) + return Empty(); + // This function assumes the layer was rendered to the scene itself. There is another + // function + // for completely offscreen layers that don't get rendered to the scene. + bool wasRenderToTarget(theLayerData->m_Layer.m_Flags.IsLayerRenderToTarget()); + if (wasRenderToTarget == false || theLayerData->m_Camera == NULL + || theLayerData->m_LayerPrepResult.hasValue() == false + || theLayerData->m_LastFrameOffscreenRenderer.mPtr != NULL) + return Empty(); + + QT3DSVec2 theMouseCoords(inMouseCoords); + QT3DSVec2 theViewportDimensions(inViewportDimensions); + + for (QT3DSU32 idx = 0, end = inMapperObjects.size(); idx < end; ++idx) { + SGraphObject ¤tObject = *inMapperObjects[idx]; + if (currentObject.m_Type == GraphObjectTypes::Layer) { + // The layer knows its viewport so it can take the information directly. + // This is extremely counter intuitive but a good sign. + } else if (currentObject.m_Type == GraphObjectTypes::Image) { + SImage &theImage = static_cast<SImage &>(currentObject); + SModel *theParentModel = NULL; + if (theImage.m_Parent + && theImage.m_Parent->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theMaterial = + static_cast<SDefaultMaterial *>(theImage.m_Parent); + if (theMaterial) { + theParentModel = theMaterial->m_Parent; + } + } + if (theParentModel == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + NVBounds3 theModelBounds = theParentModel->GetBounds( + GetQt3DSContext().GetBufferManager(), GetQt3DSContext().GetPathManager(), false); + + if (theModelBounds.isEmpty()) { + QT3DS_ASSERT(false); + return Empty(); + } + Option<QT3DSVec2> relativeHit = + FacePosition(*theParentModel, theModelBounds, theParentModel->m_GlobalTransform, + theViewportDimensions, theMouseCoords, NVDataRef<SGraphObject *>(), + SBasisPlanes::XY); + if (relativeHit.isEmpty()) { + return Empty(); + } + Qt3DSRenderPickSubResult theResult = ConstructSubResult(theImage); + QT3DSVec2 hitInUVSpace = (*relativeHit) + QT3DSVec2(.5f, .5f); + eastl::pair<QT3DSVec2, QT3DSVec2> mouseAndViewport = + GetMouseCoordsAndViewportFromSubObject(hitInUVSpace, theResult); + theMouseCoords = mouseAndViewport.first; + theViewportDimensions = mouseAndViewport.second; + } + } + + Option<SRay> theHitRay = theLayerData->m_LayerPrepResult->GetPickRay( + theMouseCoords, theViewportDimensions, false); + if (theHitRay.hasValue() == false) + return Empty(); + + // Scale the mouse coords to change them into the camera's numerical space. + SRay thePickRay = *theHitRay; + Option<QT3DSVec2> newValue = thePickRay.GetRelative(inGlobalTransform, inBounds, inPlane); + return newValue; + } + + Qt3DSRenderPickResult + Qt3DSRendererImpl::PickOffscreenLayer(SLayer &/*inLayer*/, const QT3DSVec2 & /*inViewportDimensions*/ + , + const QT3DSVec2 & /*inMouseCoords*/ + , + bool /*inPickEverything*/) + { + return Qt3DSRenderPickResult(); + } + + QT3DSVec3 Qt3DSRendererImpl::UnprojectToPosition(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec2 &inMouseVec) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } // QT3DS_ASSERT( false ); return QT3DSVec3(0,0,0); } + + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + QT3DSVec2 theDims((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + SRay theRay = thePrepResult.GetPickRay(inMouseVec, theDims, true); + + return theData->m_Camera->UnprojectToPosition(inPosition, theRay); + } + + QT3DSVec3 Qt3DSRendererImpl::UnprojectWithDepth(SNode &inNode, QT3DSVec3 &, + const QT3DSVec3 &inMouseVec) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } // QT3DS_ASSERT( false ); return QT3DSVec3(0,0,0); } + + // Flip the y into gl coordinates from window coordinates. + QT3DSVec2 theMouse(inMouseVec.x, inMouseVec.y); + NVReal theDepth = inMouseVec.z; + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + SRay theRay = thePrepResult.GetPickRay( + theMouse, QT3DSVec2((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()), true); + QT3DSVec3 theTargetPosition = theRay.m_Origin + theRay.m_Direction * theDepth; + if (inNode.m_Parent != NULL && inNode.m_Parent->m_Type != GraphObjectTypes::Layer) + theTargetPosition = + inNode.m_Parent->m_GlobalTransform.getInverse().transform(theTargetPosition); + // Our default global space is right handed, so if you are left handed z means something + // opposite. + if (inNode.m_Flags.IsLeftHanded()) + theTargetPosition.z *= -1; + return theTargetPosition; + } + + QT3DSVec3 Qt3DSRendererImpl::ProjectPosition(SNode &inNode, const QT3DSVec3 &inPosition) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } + + QT3DSMat44 viewProj; + theData->m_Camera->CalculateViewProjectionMatrix(viewProj); + QT3DSVec4 projPos = viewProj.transform(QT3DSVec4(inPosition, 1.0f)); + projPos.x /= projPos.w; + projPos.y /= projPos.w; + + NVRenderRectF theViewport = theData->m_LayerPrepResult->GetLayerToPresentationViewport(); + QT3DSVec2 theDims((QT3DSF32)theViewport.m_Width, (QT3DSF32)theViewport.m_Height); + projPos.x += 1.0; + projPos.y += 1.0; + projPos.x *= 0.5; + projPos.y *= 0.5; + QT3DSVec3 cameraToObject = theData->m_Camera->GetGlobalPos() - inPosition; + projPos.z = sqrtf(cameraToObject.dot(cameraToObject)); + QT3DSVec3 mouseVec = QT3DSVec3(projPos.x, projPos.y, projPos.z); + mouseVec.x *= theDims.x; + mouseVec.y *= theDims.y; + + mouseVec.x += theViewport.m_X; + mouseVec.y += theViewport.m_Y; + + // Flip the y into window coordinates so it matches the mouse. + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + mouseVec.y = theWindow.height() - mouseVec.y; + + return mouseVec; + } + + Option<SLayerPickSetup> Qt3DSRendererImpl::GetLayerPickSetup(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QSize &inPickDims) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + QT3DSVec2 theDims((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + // The mouse is relative to the layer + Option<QT3DSVec2> theLocalMouse = GetLayerMouseCoords(*theData, inMouseCoords, theDims, false); + if (theLocalMouse.hasValue() == false) { + return Empty(); + } + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + if (thePrepResult.GetCamera() == NULL) { + return Empty(); + } + // Perform gluPickMatrix and pre-multiply it into the view projection + QT3DSMat44 theTransScale(QT3DSMat44::createIdentity()); + SCamera &theCamera(*thePrepResult.GetCamera()); + + NVRenderRectF layerToPresentation = thePrepResult.GetLayerToPresentationViewport(); + // Offsetting is already taken care of in the camera's projection. + // All we need to do is to scale and translate the image. + layerToPresentation.m_X = 0; + layerToPresentation.m_Y = 0; + QT3DSVec2 theMouse(*theLocalMouse); + // The viewport will need to center at this location + QT3DSVec2 viewportDims((QT3DSF32)inPickDims.width(), (QT3DSF32)inPickDims.height()); + QT3DSVec2 bottomLeft = + QT3DSVec2(theMouse.x - viewportDims.x / 2.0f, theMouse.y - viewportDims.y / 2.0f); + // For some reason, and I haven't figured out why yet, the offsets need to be backwards for + // this to work. + // bottomLeft.x = layerToPresentation.m_Width - bottomLeft.x; + // bottomLeft.y = layerToPresentation.m_Height - bottomLeft.y; + // Virtual rect is relative to the layer. + NVRenderRectF thePickRect(bottomLeft.x, bottomLeft.y, viewportDims.x, viewportDims.y); + QT3DSMat44 projectionPremult(QT3DSMat44::createIdentity()); + projectionPremult = render::NVRenderContext::ApplyVirtualViewportToProjectionMatrix( + projectionPremult, layerToPresentation, thePickRect); + projectionPremult = projectionPremult.getInverse(); + + QT3DSMat44 globalInverse = theCamera.m_GlobalTransform.getInverse(); + QT3DSMat44 theVP = theCamera.m_Projection * globalInverse; + // For now we won't setup the scissor, so we may be off by inPickDims at most because + // GetLayerMouseCoords will return + // false if the mouse is too far off the layer. + return SLayerPickSetup(projectionPremult, theVP, + NVRenderRect(0, 0, (QT3DSU32)layerToPresentation.m_Width, + (QT3DSU32)layerToPresentation.m_Height)); + } + + Option<NVRenderRectF> Qt3DSRendererImpl::GetLayerRect(SLayer &inLayer) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + return thePrepResult.GetLayerToPresentationViewport(); + } + + // This doesn't have to be cheap. + void Qt3DSRendererImpl::RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return; + } + theData->PrepareAndRender(inViewProjection); + } + + void Qt3DSRendererImpl::AddRenderWidget(IRenderWidget &inWidget) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inWidget.GetNode()); + if (theData) + theData->AddRenderWidget(inWidget); + } + + void Qt3DSRendererImpl::RenderLayerRect(SLayer &inLayer, const QT3DSVec3 &inColor) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData) + theData->m_BoundingRectColor = inColor; + } + + SScaleAndPosition Qt3DSRendererImpl::GetWorldToPixelScaleFactor(const SCamera &inCamera, + const QT3DSVec3 &inWorldPoint, + SLayerRenderData &inRenderData) + { + if (inCamera.m_Flags.IsOrthographic() == true) { + // There are situations where the camera can scale. + return SScaleAndPosition( + inWorldPoint, + inCamera.GetOrthographicScaleFactor( + inRenderData.m_LayerPrepResult->GetLayerToPresentationViewport(), + inRenderData.m_LayerPrepResult->GetPresentationDesignDimensions())); + } else { + QT3DSVec3 theCameraPos(0, 0, 0); + QT3DSVec3 theCameraDir(0, 0, -1); + SRay theRay(theCameraPos, inWorldPoint - theCameraPos); + NVPlane thePlane(theCameraDir, -600); + QT3DSVec3 theItemPosition(inWorldPoint); + Option<QT3DSVec3> theIntersection = theRay.Intersect(thePlane); + if (theIntersection.hasValue()) + theItemPosition = *theIntersection; + // The special number comes in from physically measuring how off we are on the screen. + QT3DSF32 theScaleFactor = (1.0f / inCamera.m_Projection.column1[1]); + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inCamera); + QT3DSU32 theHeight = theData->m_LayerPrepResult->GetTextureDimensions().height(); + QT3DSF32 theScaleMultiplier = 600.0f / ((QT3DSF32)theHeight / 2.0f); + theScaleFactor *= theScaleMultiplier; + + return SScaleAndPosition(theItemPosition, theScaleFactor); + } + } + + SScaleAndPosition Qt3DSRendererImpl::GetWorldToPixelScaleFactor(SLayer &inLayer, + const QT3DSVec3 &inWorldPoint) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return SScaleAndPosition(); + } + return GetWorldToPixelScaleFactor(*theData->m_Camera, inWorldPoint, *theData); + } + + void Qt3DSRendererImpl::ReleaseLayerRenderResources(SLayer &inLayer, const SRenderInstanceId id) + { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(&inLayer, id)); + if (theIter != m_InstanceRenderMap.end()) { + TLayerRenderList::iterator theLastFrm = eastl::find( + m_LastFrameLayers.begin(), m_LastFrameLayers.end(), theIter->second.mPtr); + if (theLastFrm != m_LastFrameLayers.end()) { + theIter->second->ResetForFrame(); + m_LastFrameLayers.erase(theLastFrm); + } + m_InstanceRenderMap.erase(theIter); + } + } + + void Qt3DSRendererImpl::RenderQuad(const QT3DSVec2 inDimensions, const QT3DSMat44 &inMVP, + NVRenderTexture2D &inQuadTexture) + { + m_Context->SetCullingEnabled(false); + SLayerSceneShader *theShader = GetSceneLayerShader(); + NVRenderContext &theContext(*m_Context); + theContext.SetActiveShader(&theShader->m_Shader); + theShader->m_MVP.Set(inMVP); + theShader->m_Dimensions.Set(inDimensions); + theShader->m_Sampler.Set(&inQuadTexture); + + GenerateXYQuad(); + theContext.SetInputAssembler(m_QuadInputAssembler); + theContext.Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::RenderQuad() + { + m_Context->SetCullingEnabled(false); + GenerateXYQuad(); + m_Context->SetInputAssembler(m_QuadInputAssembler); + m_Context->Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::RenderPointsIndirect() + { + m_Context->SetCullingEnabled(false); + GenerateXYZPoint(); + m_Context->SetInputAssembler(m_PointInputAssembler); + m_Context->DrawIndirect(NVRenderDrawMode::Points, 0); + } + + void Qt3DSRendererImpl::LayerNeedsFrameClear(SLayerRenderData &inLayer) + { + m_LastFrameLayers.push_back(&inLayer); + } + + void Qt3DSRendererImpl::BeginLayerDepthPassRender(SLayerRenderData &inLayer) + { + m_CurrentLayer = &inLayer; + } + + void Qt3DSRendererImpl::EndLayerDepthPassRender() { m_CurrentLayer = NULL; } + + void Qt3DSRendererImpl::BeginLayerRender(SLayerRenderData &inLayer) + { + m_CurrentLayer = &inLayer; + // Remove all of the shaders from the layer shader set + // so that we can only apply the camera and lighting properties to + // shaders that are in the layer. + m_LayerShaders.clear(); + } + void Qt3DSRendererImpl::EndLayerRender() { m_CurrentLayer = NULL; } + +// Allocate an object that lasts only this frame. +#define RENDER_FRAME_NEW(type) \ + new (m_PerFrameAllocator.m_FastAllocator.allocate(sizeof(type), __FILE__, __LINE__)) type + + void Qt3DSRendererImpl::PrepareImageForIbl(SImage &inImage) + { + if (inImage.m_TextureData.m_Texture && inImage.m_TextureData.m_Texture->GetNumMipmaps() < 1) + inImage.m_TextureData.m_Texture->GenerateMipmaps(); + } + + bool NodeContainsBoneRoot(SNode &childNode, QT3DSI32 rootID) + { + for (SNode *childChild = childNode.m_FirstChild; childChild != NULL; + childChild = childChild->m_NextSibling) { + if (childChild->m_SkeletonId == rootID) + return true; + } + + return false; + } + + void FillBoneIdNodeMap(SNode &childNode, nvhash_map<long, SNode *> &ioMap) + { + if (childNode.m_SkeletonId >= 0) + ioMap[childNode.m_SkeletonId] = &childNode; + for (SNode *childChild = childNode.m_FirstChild; childChild != NULL; + childChild = childChild->m_NextSibling) + FillBoneIdNodeMap(*childChild, ioMap); + } + + bool Qt3DSRendererImpl::PrepareTextureAtlasForRender() + { + ITextTextureAtlas *theTextureAtlas = m_qt3dsContext.GetTextureAtlas(); + if (theTextureAtlas == NULL) + return false; + + // this is a one time creation + if (!theTextureAtlas->IsInitialized()) { + NVRenderContext &theContext(*m_Context); + NVScopedRefCounted<NVRenderVertexBuffer> mVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> mInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> mAttribLayout; + // temporay FB + using qt3ds::render::NVRenderContextScopedProperty; + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + *m_Context, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + + ITextRenderer &theTextRenderer(*m_qt3dsContext.GetOnscreenTextRenderer()); + TTextTextureAtlasDetailsAndTexture theResult = theTextureAtlas->PrepareTextureAtlas(); + if (!theResult.first.m_EntryCount) { + QT3DS_ASSERT(theResult.first.m_EntryCount); + return false; + } + + // generate the index buffer we need + GenerateXYQuad(); + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_uv", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + // create our attribute layout + mAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + NVRenderFrameBuffer *theAtlasFB( + m_qt3dsContext.GetResourceManager().AllocateFrameBuffer()); + theAtlasFB->Attach(NVRenderFrameBufferAttachments::Color0, *theResult.second); + m_qt3dsContext.GetRenderContext().SetRenderTarget(theAtlasFB); + + // this texture contains our single entries + NVRenderTexture2D *theTexture = nullptr; + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theTexture = m_qt3dsContext.GetResourceManager() + .AllocateTexture2D(32, 32, NVRenderTextureFormats::RGBA8); + } else { + theTexture = m_qt3dsContext.GetResourceManager() + .AllocateTexture2D(32, 32, NVRenderTextureFormats::Alpha8); + } + m_Context->SetClearColor(QT3DSVec4(0, 0, 0, 0)); + m_Context->Clear(NVRenderClearValues::Color); + m_Context->SetDepthTestEnabled(false); + m_Context->SetScissorTestEnabled(false); + m_Context->SetCullingEnabled(false); + m_Context->SetBlendingEnabled(false); + m_Context->SetViewport( + NVRenderRect(0, 0, theResult.first.m_TextWidth, theResult.first.m_TextHeight)); + + SCamera theCamera; + theCamera.m_ClipNear = -1.0; + theCamera.m_ClipFar = 1.0; + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theTextureDims((QT3DSF32)theResult.first.m_TextWidth, + (QT3DSF32)theResult.first.m_TextHeight); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theResult.first.m_TextWidth, theResult.first.m_TextHeight), + theTextureDims); + // We want a 2D lower left projection + QT3DSF32 *writePtr(theCamera.m_Projection.front()); + writePtr[12] = -1; + writePtr[13] = -1; + + // generate render stuff + // We dynamicall update the vertex buffer + QT3DSF32 tempBuf[20]; + QT3DSF32 *bufPtr = tempBuf; + QT3DSU32 bufSize = 20 * sizeof(QT3DSF32); // 4 vertices 3 pos 2 tex + NVDataRef<QT3DSU8> vertData((QT3DSU8 *)bufPtr, bufSize); + mVertexBuffer = theContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, 20 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), vertData); + QT3DSU32 strides = mVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + mInputAssembler = theContext.CreateInputAssembler( + mAttribLayout, toConstDataRef(&mVertexBuffer.mPtr, 1), m_QuadIndexBuffer.mPtr, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + + NVRenderShaderProgram *theShader = GetTextAtlasEntryShader(); + STextShader theTextShader(*theShader); + + if (theShader) { + theContext.SetActiveShader(theShader); + theTextShader.m_MVP.Set(theCamera.m_Projection); + + // we are going through all entries and render to the FBO + for (QT3DSU32 i = 0; i < theResult.first.m_EntryCount; i++) { + STextTextureAtlasEntryDetails theDetails = + theTextRenderer.RenderAtlasEntry(i, *theTexture); + // update vbo + // we need to mirror coordinates + QT3DSF32 x1 = (QT3DSF32)theDetails.m_X; + QT3DSF32 x2 = (QT3DSF32)theDetails.m_X + theDetails.m_TextWidth; + QT3DSF32 y1 = (QT3DSF32)theDetails.m_Y; + QT3DSF32 y2 = (QT3DSF32)theDetails.m_Y + theDetails.m_TextHeight; + + QT3DSF32 box[4][5] = { + { x1, y1, 0, 0, 1 }, + { x1, y2, 0, 0, 0 }, + { x2, y2, 0, 1, 0 }, + { x2, y1, 0, 1, 1 }, + }; + + NVDataRef<QT3DSU8> vertData((QT3DSU8 *)box, bufSize); + mVertexBuffer->UpdateBuffer(vertData, false); + + theTextShader.m_Sampler.Set(theTexture); + + theContext.SetInputAssembler(mInputAssembler); + theContext.Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), + 0); + } + } + + m_qt3dsContext.GetResourceManager().Release(*theTexture); + m_qt3dsContext.GetResourceManager().Release(*theAtlasFB); + + return true; + } + + return theTextureAtlas->IsInitialized(); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::GetLayerMouseCoords(SLayerRenderData &inLayerRenderData, + const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect) const + { + if (inLayerRenderData.m_LayerPrepResult.hasValue()) + return inLayerRenderData.m_LayerPrepResult->GetLayerMouseCoords( + inMouseCoords, inViewportDimensions, forceImageIntersect); + return Empty(); + } + + void Qt3DSRendererImpl::GetLayerHitObjectList(SLayerRenderData &inLayerRenderData, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inPresCoords, bool inPickEverything, + TPickResultArray &outIntersectionResult, + NVAllocatorCallback &inTempAllocator) + { + // This function assumes the layer was rendered to the scene itself. There is another + // function + // for completely offscreen layers that don't get rendered to the scene. + bool wasRenderToTarget(inLayerRenderData.m_Layer.m_Flags.IsLayerRenderToTarget()); + if (wasRenderToTarget && inLayerRenderData.m_Camera != NULL) { + Option<SRay> theHitRay; + if (inLayerRenderData.m_LayerPrepResult.hasValue()) { + theHitRay = inLayerRenderData.m_LayerPrepResult->GetPickRay( + inPresCoords, inViewportDimensions, false, m_Context->isSceneCameraView()); + } + if (inLayerRenderData.m_LastFrameOffscreenRenderer.mPtr == NULL) { + if (theHitRay.hasValue()) { + // Scale the mouse coords to change them into the camera's numerical space. + SRay thePickRay = *theHitRay; + for (QT3DSU32 idx = inLayerRenderData.m_OpaqueObjects.size(), end = 0; idx > end; + --idx) { + SRenderableObject *theRenderableObject = + inLayerRenderData.m_OpaqueObjects[idx - 1]; + if (inPickEverything + || theRenderableObject->m_RenderableFlags.GetPickable()) + IntersectRayWithSubsetRenderable(thePickRay, *theRenderableObject, + outIntersectionResult, + inTempAllocator); + } + for (QT3DSU32 idx = inLayerRenderData.m_TransparentObjects.size(), end = 0; + idx > end; --idx) { + SRenderableObject *theRenderableObject = + inLayerRenderData.m_TransparentObjects[idx - 1]; + if (inPickEverything + || theRenderableObject->m_RenderableFlags.GetPickable()) + IntersectRayWithSubsetRenderable(thePickRay, *theRenderableObject, + outIntersectionResult, + inTempAllocator); + } + } + } else { + IGraphObjectPickQuery *theQuery = + inLayerRenderData.m_LastFrameOffscreenRenderer->GetGraphObjectPickQuery(this); + if (theQuery) { + Qt3DSRenderPickResult theResult = + theQuery->Pick(inPresCoords, inViewportDimensions, inPickEverything); + if (theResult.m_HitObject) { + theResult.m_OffscreenRenderer = + inLayerRenderData.m_LastFrameOffscreenRenderer; + outIntersectionResult.push_back(theResult); + } + } else + inLayerRenderData.m_LastFrameOffscreenRenderer->Pick(inPresCoords, + inViewportDimensions, + this); + } + } + } + + static inline Qt3DSRenderPickSubResult ConstructSubResult(SRenderableImage &inImage) + { + return ConstructSubResult(inImage.m_Image); + } + + void Qt3DSRendererImpl::IntersectRayWithSubsetRenderable( + const SRay &inRay, SRenderableObject &inRenderableObject, + TPickResultArray &outIntersectionResultList, NVAllocatorCallback &inTempAllocator) + { + Option<SRayIntersectionResult> theIntersectionResultOpt(inRay.IntersectWithAABB( + inRenderableObject.m_GlobalTransform, inRenderableObject.m_Bounds)); + if (theIntersectionResultOpt.hasValue() == false) + return; + SRayIntersectionResult &theResult(*theIntersectionResultOpt); + + // Leave the coordinates relative for right now. + const SGraphObject *thePickObject = NULL; + if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + thePickObject = + &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; + else if (inRenderableObject.m_RenderableFlags.IsPath()) + thePickObject = &static_cast<SPathRenderable *>(&inRenderableObject)->m_Path; + + if (thePickObject != NULL) { + outIntersectionResultList.push_back(Qt3DSRenderPickResult( + *thePickObject, theResult.m_RayLengthSquared, theResult.m_RelXY)); + + // For subsets, we know we can find images on them which may have been the result + // of rendering a sub-presentation. + if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) { + Qt3DSRenderPickSubResult *theLastResult = NULL; + for (SRenderableImage *theImage = + static_cast<SSubsetRenderable *>(&inRenderableObject)->m_FirstImage; + theImage != NULL; theImage = theImage->m_NextImage) { + if (theImage->m_Image.m_LastFrameOffscreenRenderer != NULL + && theImage->m_Image.m_TextureData.m_Texture != NULL) { + Qt3DSRenderPickSubResult *theSubResult = + (Qt3DSRenderPickSubResult *)inTempAllocator.allocate( + sizeof(Qt3DSRenderPickSubResult), "Qt3DSRenderPickSubResult", + __FILE__, __LINE__); + + new (theSubResult) Qt3DSRenderPickSubResult(ConstructSubResult(*theImage)); + if (theLastResult == NULL) + outIntersectionResultList.back().m_FirstSubObject = theSubResult; + else + theLastResult->m_NextSibling = theSubResult; + theLastResult = theSubResult; + } + } + } + } + } + +#ifndef EA_PLATFORM_WINDOWS +#define _snprintf snprintf +#endif + + NVRenderShaderProgram *Qt3DSRendererImpl::CompileShader(CRegisteredString inName, + const char8_t *inVert, + const char8_t *inFrag) + { + GetProgramGenerator().BeginProgram(); + GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)->Append(inVert); + GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)->Append(inFrag); + return GetProgramGenerator().CompileGeneratedShader(inName); + } + + const QT3DSF32 MINATTENUATION = 0; + const QT3DSF32 MAXATTENUATION = 1000; + + QT3DSF32 ClampFloat(QT3DSF32 value, QT3DSF32 min, QT3DSF32 max) + { + return value < min ? min : ((value > max) ? max : value); + } + + QT3DSF32 TranslateConstantAttenuation(QT3DSF32 attenuation) { return attenuation * .01f; } + + QT3DSF32 TranslateLinearAttenuation(QT3DSF32 attenuation) + { + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0001f; + } + + QT3DSF32 TranslateQuadraticAttenuation(QT3DSF32 attenuation) + { + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0000001f; + } + + SShaderGeneratorGeneratedShader *Qt3DSRendererImpl::GetShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet) + { + if (m_CurrentLayer == NULL) { + QT3DS_ASSERT(false); + return NULL; + } + TShaderMap::iterator theFind = m_Shaders.find(inRenderable.m_ShaderDescription); + SShaderGeneratorGeneratedShader *retval = NULL; + if (theFind == m_Shaders.end()) { + // Generate the shader. + NVRenderShaderProgram *theShader(GenerateShader(inRenderable, inFeatureSet)); + if (theShader) { + SShaderGeneratorGeneratedShader *theGeneratedShader = + (SShaderGeneratorGeneratedShader *)m_Context->GetAllocator().allocate( + sizeof(SShaderGeneratorGeneratedShader), "SShaderGeneratorGeneratedShader", + __FILE__, __LINE__); + new (theGeneratedShader) SShaderGeneratorGeneratedShader( + m_StringTable->RegisterStr(m_GeneratedShaderString.c_str()), *theShader); + m_Shaders.insert(make_pair(inRenderable.m_ShaderDescription, theGeneratedShader)); + retval = theGeneratedShader; + } + // We still insert something because we don't to attempt to generate the same bad shader + // twice. + else + m_Shaders.insert(make_pair(inRenderable.m_ShaderDescription, + (SShaderGeneratorGeneratedShader *)NULL)); + } else + retval = theFind->second; + + if (retval != NULL) { + if (!m_LayerShaders.contains(*retval)) { + m_LayerShaders.insert(*retval); + } + if (m_CurrentLayer && m_CurrentLayer->m_Camera) { + SCamera &theCamera(*m_CurrentLayer->m_Camera); + if (m_CurrentLayer->m_CameraDirection.hasValue() == false) + m_CurrentLayer->m_CameraDirection = theCamera.GetScalingCorrectDirection(); + } + } + return retval; + } + static QT3DSVec3 g_fullScreenRectFace[] = { + QT3DSVec3(-1, -1, 0), QT3DSVec3(-1, 1, 0), QT3DSVec3(1, 1, 0), QT3DSVec3(1, -1, 0), + }; + + static QT3DSVec2 g_fullScreenRectUVs[] = { QT3DSVec2(0, 0), QT3DSVec2(0, 1), QT3DSVec2(1, 1), + QT3DSVec2(1, 0) }; + + void Qt3DSRendererImpl::GenerateXYQuad() + { + if (m_QuadInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + QT3DSF32 tempBuf[20]; + QT3DSF32 *bufPtr = tempBuf; + QT3DSVec3 *facePtr(g_fullScreenRectFace); + QT3DSVec2 *uvPtr(g_fullScreenRectUVs); + for (int j = 0; j < 4; j++, ++facePtr, ++uvPtr, bufPtr += 5) { + bufPtr[0] = facePtr->x; + bufPtr[1] = facePtr->y; + bufPtr[2] = facePtr->z; + bufPtr[3] = uvPtr->x; + bufPtr[4] = uvPtr->y; + } + m_QuadVertexBuffer = m_Context->CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, 20 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), toU8DataRef(tempBuf, 20)); + + QT3DSU8 indexData[] = { + 0, 1, 2, 0, 2, 3, + }; + m_QuadIndexBuffer = m_Context->CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, qt3ds::render::NVRenderComponentTypes::QT3DSU8, + sizeof(indexData), toU8DataRef(indexData, sizeof(indexData))); + + // create our attribute layout + m_QuadAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_QuadVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_QuadInputAssembler = m_Context->CreateInputAssembler( + m_QuadAttribLayout, toConstDataRef(&m_QuadVertexBuffer.mPtr, 1), m_QuadIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + void Qt3DSRendererImpl::GenerateXYZPoint() + { + if (m_PointInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + QT3DSF32 tempBuf[5]; + tempBuf[0] = tempBuf[1] = tempBuf[2] = 0.0; + tempBuf[3] = tempBuf[4] = 0.0; + + m_PointVertexBuffer = m_Context->CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, 5 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), toU8DataRef(tempBuf, 5)); + + // create our attribute layout + m_PointAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_PointVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_PointInputAssembler = m_Context->CreateInputAssembler( + m_PointAttribLayout, toConstDataRef(&m_PointVertexBuffer.mPtr, 1), NULL, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + eastl::pair<NVRenderVertexBuffer *, NVRenderIndexBuffer *> Qt3DSRendererImpl::GetXYQuad() + { + if (!m_QuadInputAssembler) + GenerateXYQuad(); + + return eastl::make_pair(m_QuadVertexBuffer.mPtr, m_QuadIndexBuffer.mPtr); + } + + SLayerGlobalRenderProperties Qt3DSRendererImpl::GetLayerGlobalRenderProperties() + { + SLayerRenderData &theData = *m_CurrentLayer; + SLayer &theLayer = theData.m_Layer; + if (theData.m_CameraDirection.hasValue() == false) + theData.m_CameraDirection = theData.m_Camera->GetScalingCorrectDirection(); + + return SLayerGlobalRenderProperties( + theLayer, *theData.m_Camera, *theData.m_CameraDirection, theData.m_Lights, + theData.m_LightDirections, theData.m_ShadowMapManager.mPtr, theData.m_LayerDepthTexture, + theData.m_LayerSsaoTexture, theLayer.m_LightProbe, theLayer.m_LightProbe2, + theLayer.m_ProbeHorizon, theLayer.m_ProbeBright, theLayer.m_Probe2Window, + theLayer.m_Probe2Pos, theLayer.m_Probe2Fade, theLayer.m_ProbeFov); + } + + void Qt3DSRendererImpl::GenerateXYQuadStrip() + { + if (m_QuadStripInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + // this buffer is filled dynmically + m_QuadStripVertexBuffer = + m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, 0, + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32) // stride + , + NVDataRef<QT3DSU8>()); + + // create our attribute layout + m_QuadStripAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_QuadStripVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_QuadStripInputAssembler = m_Context->CreateInputAssembler( + m_QuadStripAttribLayout, toConstDataRef(&m_QuadStripVertexBuffer.mPtr, 1), NULL, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + void Qt3DSRendererImpl::UpdateCbAoShadow(const SLayer *pLayer, const SCamera *pCamera, + CResourceTexture2D &inDepthTexture) + { + if (m_Context->GetConstantBufferSupport()) { + CRegisteredString theName = m_Context->GetStringTable().RegisterStr("cbAoShadow"); + NVRenderConstantBuffer *pCB = m_Context->GetConstantBuffer(theName); + + if (!pCB) { + // the size is determined automatically later on + pCB = m_Context->CreateConstantBuffer( + theName, qt3ds::render::NVRenderBufferUsageType::Static, 0, NVDataRef<QT3DSU8>()); + if (!pCB) { + QT3DS_ASSERT(false); + return; + } + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + + // Add paramters. Note should match the appearance in the shader program + pCB->AddParam(m_Context->GetStringTable().RegisterStr("ao_properties"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("ao_properties2"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("shadow_properties"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("aoScreenConst"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("UvToEyeConst"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + } + + // update values + QT3DSVec4 aoProps(pLayer->m_AoStrength * 0.01f, pLayer->m_AoDistance * 0.4f, + pLayer->m_AoSoftness * 0.02f, pLayer->m_AoBias); + pCB->UpdateParam("ao_properties", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoProps, 1)); + QT3DSVec4 aoProps2((QT3DSF32)pLayer->m_AoSamplerate, (pLayer->m_AoDither) ? 1.0f : 0.0f, 0.0f, + 0.0f); + pCB->UpdateParam("ao_properties2", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoProps2, 1)); + QT3DSVec4 shadowProps(pLayer->m_ShadowStrength * 0.01f, pLayer->m_ShadowDist, + pLayer->m_ShadowSoftness * 0.01f, pLayer->m_ShadowBias); + pCB->UpdateParam("shadow_properties", NVDataRef<QT3DSU8>((QT3DSU8 *)&shadowProps, 1)); + + QT3DSF32 R2 = pLayer->m_AoDistance * pLayer->m_AoDistance * 0.16f; + QT3DSF32 rw = 100, rh = 100; + + if (inDepthTexture && inDepthTexture.GetTexture()) { + rw = (QT3DSF32)inDepthTexture.GetTexture()->GetTextureDetails().m_Width; + rh = (QT3DSF32)inDepthTexture.GetTexture()->GetTextureDetails().m_Height; + } + QT3DSF32 fov = (pCamera) ? pCamera->verticalFov(rw / rh) : 1.0f; + QT3DSF32 tanHalfFovY = tanf(0.5f * fov * (rh / rw)); + QT3DSF32 invFocalLenX = tanHalfFovY * (rw / rh); + + QT3DSVec4 aoScreenConst(1.0f / R2, rh / (2.0f * tanHalfFovY), 1.0f / rw, 1.0f / rh); + pCB->UpdateParam("aoScreenConst", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoScreenConst, 1)); + QT3DSVec4 UvToEyeConst(2.0f * invFocalLenX, -2.0f * tanHalfFovY, -invFocalLenX, + tanHalfFovY); + pCB->UpdateParam("UvToEyeConst", NVDataRef<QT3DSU8>((QT3DSU8 *)&UvToEyeConst, 1)); + + // update buffer to hardware + pCB->Update(); + } + } + + // widget context implementation + + NVRenderVertexBuffer &Qt3DSRendererImpl::GetOrCreateVertexBuffer(CRegisteredString &inStr, + QT3DSU32 stride, + NVConstDataRef<QT3DSU8> bufferData) + { + NVRenderVertexBuffer *retval = GetVertexBuffer(inStr); + if (retval) { + // we update the buffer + retval->UpdateBuffer(bufferData, false); + return *retval; + } + retval = m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, + bufferData.size(), stride, bufferData); + m_WidgetVertexBuffers.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + NVRenderIndexBuffer & + Qt3DSRendererImpl::GetOrCreateIndexBuffer(CRegisteredString &inStr, + qt3ds::render::NVRenderComponentTypes::Enum componentType, + size_t size, NVConstDataRef<QT3DSU8> bufferData) + { + NVRenderIndexBuffer *retval = GetIndexBuffer(inStr); + if (retval) { + // we update the buffer + retval->UpdateBuffer(bufferData, false); + return *retval; + } + + retval = m_Context->CreateIndexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, + componentType, size, bufferData); + m_WidgetIndexBuffers.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + + NVRenderAttribLayout &Qt3DSRendererImpl::CreateAttributeLayout( + NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> attribs) + { + // create our attribute layout + NVRenderAttribLayout *theAttribLAyout = m_Context->CreateAttributeLayout(attribs); + return *theAttribLAyout; + } + + NVRenderInputAssembler &Qt3DSRendererImpl::GetOrCreateInputAssembler( + CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef<NVRenderVertexBuffer *> buffers, const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef<QT3DSU32> strides, NVConstDataRef<QT3DSU32> offsets) + { + NVRenderInputAssembler *retval = GetInputAssembler(inStr); + if (retval) + return *retval; + + retval = + m_Context->CreateInputAssembler(attribLayout, buffers, indexBuffer, strides, offsets); + m_WidgetInputAssembler.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + + NVRenderVertexBuffer *Qt3DSRendererImpl::GetVertexBuffer(CRegisteredString &inStr) + { + TStrVertBufMap::iterator theIter = m_WidgetVertexBuffers.find(inStr); + if (theIter != m_WidgetVertexBuffers.end()) + return theIter->second; + return NULL; + } + + NVRenderIndexBuffer *Qt3DSRendererImpl::GetIndexBuffer(CRegisteredString &inStr) + { + TStrIndexBufMap::iterator theIter = m_WidgetIndexBuffers.find(inStr); + if (theIter != m_WidgetIndexBuffers.end()) + return theIter->second; + return NULL; + } + + NVRenderInputAssembler *Qt3DSRendererImpl::GetInputAssembler(CRegisteredString &inStr) + { + TStrIAMap::iterator theIter = m_WidgetInputAssembler.find(inStr); + if (theIter != m_WidgetInputAssembler.end()) + return theIter->second; + return NULL; + } + + NVRenderShaderProgram *Qt3DSRendererImpl::GetShader(CRegisteredString inStr) + { + TStrShaderMap::iterator theIter = m_WidgetShaders.find(inStr); + if (theIter != m_WidgetShaders.end()) + return theIter->second; + return NULL; + } + + NVRenderShaderProgram *Qt3DSRendererImpl::CompileAndStoreShader(CRegisteredString inStr) + { + NVRenderShaderProgram *newProgram = GetProgramGenerator().CompileGeneratedShader(inStr); + if (newProgram) + m_WidgetShaders.insert(eastl::make_pair(inStr, newProgram)); + return newProgram; + } + + IShaderProgramGenerator &Qt3DSRendererImpl::GetProgramGenerator() + { + return m_qt3dsContext.GetShaderProgramGenerator(); + } + + STextDimensions Qt3DSRendererImpl::MeasureText(const STextRenderInfo &inText) + { + if (m_qt3dsContext.GetTextRenderer() != NULL) + return m_qt3dsContext.GetTextRenderer()->MeasureText(inText, 0); + return STextDimensions(); + } + + void Qt3DSRendererImpl::RenderText(const STextRenderInfo &inText, const QT3DSVec3 &inTextColor, + const QT3DSVec3 &inBackgroundColor, const QT3DSMat44 &inMVP) + { + if (m_qt3dsContext.GetTextRenderer() != NULL) { + ITextRenderer &theTextRenderer(*m_qt3dsContext.GetTextRenderer()); + NVRenderTexture2D *theTexture = m_qt3dsContext.GetResourceManager().AllocateTexture2D( + 32, 32, NVRenderTextureFormats::RGBA8); + STextTextureDetails theTextTextureDetails = + theTextRenderer.RenderText(inText, *theTexture); + STextRenderHelper theTextHelper(GetTextWidgetShader()); + if (theTextHelper.m_Shader != NULL) { + m_qt3dsContext.GetRenderContext().SetBlendingEnabled(false); + STextScaleAndOffset theScaleAndOffset(*theTexture, theTextTextureDetails, inText); + theTextHelper.m_Shader->Render(*theTexture, theScaleAndOffset, + QT3DSVec4(inTextColor, 1.0f), inMVP, QT3DSVec2(0, 0), + GetContext(), theTextHelper.m_QuadInputAssembler, + theTextHelper.m_QuadInputAssembler.GetIndexCount(), + theTextTextureDetails, inBackgroundColor); + } + m_qt3dsContext.GetResourceManager().Release(*theTexture); + } + } + + void Qt3DSRendererImpl::RenderText2D(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) + { + if (m_qt3dsContext.GetOnscreenTextRenderer() != NULL) { + GenerateXYQuadStrip(); + + if (PrepareTextureAtlasForRender()) { + TTextRenderAtlasDetailsAndTexture theRenderTextDetails; + ITextTextureAtlas *theTextureAtlas = m_qt3dsContext.GetTextureAtlas(); + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + + const wchar_t *wText = m_StringTable->GetWideStr(text); + STextRenderInfo theInfo; + theInfo.m_Text = m_StringTable->RegisterStr(wText); + theInfo.m_FontSize = 20; + // text scale 2% of screen we don't scale Y though because it becomes unreadable + theInfo.m_ScaleX = (theWindow.width() / 100.0f) * 1.5f / (theInfo.m_FontSize); + theInfo.m_ScaleY = 1.0f; + + theRenderTextDetails = theTextureAtlas->RenderText(theInfo); + + if (theRenderTextDetails.first.m_Vertices.size()) { + STextRenderHelper theTextHelper(GetOnscreenTextShader()); + if (theTextHelper.m_Shader != NULL) { + // setup 2D projection + SCamera theCamera; + theCamera.m_ClipNear = -1.0; + theCamera.m_ClipFar = 1.0; + + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theWindowDim((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theWindow.width(), theWindow.height()), + theWindowDim); + // We want a 2D lower left projection + QT3DSF32 *writePtr(theCamera.m_Projection.front()); + writePtr[12] = -1; + writePtr[13] = -1; + + // upload vertices + m_QuadStripVertexBuffer->UpdateBuffer(theRenderTextDetails.first.m_Vertices, + false); + + theTextHelper.m_Shader->Render2D( + *theRenderTextDetails.second, QT3DSVec4(inColor, 1.0f), + theCamera.m_Projection, GetContext(), + theTextHelper.m_QuadInputAssembler, + theRenderTextDetails.first.m_VertexCount, QT3DSVec2(x, y)); + } + // we release the memory here + QT3DS_FREE(m_Context->GetAllocator(), + theRenderTextDetails.first.m_Vertices.begin()); + } + } + } + } + + void Qt3DSRendererImpl::RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) + { + if (!IsLayerGpuProfilingEnabled()) + return; + + char messageLine[1024]; + TInstanceRenderMap::const_iterator theIter; + + QT3DSF32 startY = y; + + for (theIter = m_InstanceRenderMap.begin(); theIter != m_InstanceRenderMap.end(); theIter++) { + QT3DSF32 startX = x; + const SLayerRenderData *theLayerRenderData = theIter->second; + const SLayer *theLayer = &theLayerRenderData->m_Layer; + + if (theLayer->m_Flags.IsActive() && theLayerRenderData->m_LayerProfilerGpu.mPtr) { + const IRenderProfiler::TStrIDVec &idList = + theLayerRenderData->m_LayerProfilerGpu->GetTimerIDs(); + if (!idList.empty()) { + startY -= 22; + startX += 20; + RenderText2D(startX, startY, inColor, theLayer->m_Id); + IRenderProfiler::TStrIDVec::const_iterator theIdIter = idList.begin(); + for (theIdIter = idList.begin(); theIdIter != idList.end(); theIdIter++) { + startY -= 22; + sprintf(messageLine, "%s: %.3f ms", theIdIter->c_str(), + theLayerRenderData->m_LayerProfilerGpu->GetElapsedTime(*theIdIter)); + RenderText2D(startX + 20, startY, inColor, messageLine); + } + } + } + } + } + + // Given a node and a point in the node's local space (most likely its pivot point), we return + // a normal matrix so you can get the axis out, a transformation from node to camera + // a new position and a floating point scale factor so you can render in 1/2 perspective mode + // or orthographic mode if you would like to. + SWidgetRenderInformation + Qt3DSRendererImpl::GetWidgetRenderInformation(SNode &inNode, const QT3DSVec3 &inPos, + RenderWidgetModes::Enum inWidgetMode) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inNode); + SCamera *theCamera = theData->m_Camera; + if (theCamera == NULL || theData->m_LayerPrepResult.hasValue() == false) { + QT3DS_ASSERT(false); + return SWidgetRenderInformation(); + } + QT3DSMat44 theGlobalTransform(QT3DSMat44::createIdentity()); + if (inNode.m_Parent != NULL && inNode.m_Parent->m_Type != GraphObjectTypes::Layer + && !inNode.m_Flags.IsIgnoreParentTransform()) + theGlobalTransform = inNode.m_Parent->m_GlobalTransform; + QT3DSMat44 theCameraInverse(theCamera->m_GlobalTransform.getInverse()); + QT3DSMat44 theNodeParentToCamera; + if (inWidgetMode == RenderWidgetModes::Local) + theNodeParentToCamera = theCameraInverse * theGlobalTransform; + else + theNodeParentToCamera = theCameraInverse; + + QT3DSMat33 theNormalMat(theNodeParentToCamera.column0.getXYZ(), + theNodeParentToCamera.column1.getXYZ(), + theNodeParentToCamera.column2.getXYZ()); + theNormalMat = theNormalMat.getInverse().getTranspose(); + theNormalMat.column0.normalize(); + theNormalMat.column1.normalize(); + theNormalMat.column2.normalize(); + + QT3DSMat44 theTranslation(QT3DSMat44::createIdentity()); + theTranslation.column3.x = inNode.m_Position.x; + theTranslation.column3.y = inNode.m_Position.y; + theTranslation.column3.z = inNode.m_Position.z; + theTranslation.column3.z *= -1.0f; + + theGlobalTransform = theGlobalTransform * theTranslation; + + QT3DSMat44 theNodeToParentPlusTranslation = theCameraInverse * theGlobalTransform; + QT3DSVec3 thePos = theNodeToParentPlusTranslation.transform(inPos); + SScaleAndPosition theScaleAndPos = GetWorldToPixelScaleFactor(*theCamera, thePos, *theData); + QT3DSMat33 theLookAtMatrix(QT3DSMat33::createIdentity()); + if (theCamera->m_Flags.IsOrthographic() == false) { + QT3DSVec3 theNodeToCamera = theScaleAndPos.m_Position; + theNodeToCamera.normalize(); + QT3DSVec3 theOriginalAxis = QT3DSVec3(0, 0, -1); + QT3DSVec3 theRotAxis = theOriginalAxis.cross(theNodeToCamera); + QT3DSF32 theAxisLen = theRotAxis.normalize(); + if (theAxisLen > .05f) { + QT3DSF32 theRotAmount = acos(theOriginalAxis.dot(theNodeToCamera)); + QT3DSQuat theQuat(theRotAmount, theRotAxis); + theLookAtMatrix = QT3DSMat33(theQuat); + } + } + QT3DSVec3 thePosInWorldSpace = theGlobalTransform.transform(inPos); + QT3DSVec3 theCameraPosInWorldSpace = theCamera->GetGlobalPos(); + QT3DSVec3 theCameraOffset = thePosInWorldSpace - theCameraPosInWorldSpace; + QT3DSVec3 theDir = theCameraOffset; + theDir.normalize(); + // Things should be 600 units from the camera, as that is how all of our math is setup. + theCameraOffset = 600.0f * theDir; + return SWidgetRenderInformation( + theNormalMat, theNodeParentToCamera, theCamera->m_Projection, theCamera->m_Projection, + theLookAtMatrix, theCameraInverse, theCameraOffset, theScaleAndPos.m_Position, + theScaleAndPos.m_Scale, *theCamera); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::GetLayerMouseCoords(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect) const + { + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inLayer); + return GetLayerMouseCoords(*theData, inMouseCoords, inViewportDimensions, + forceImageIntersect); + } + + bool IQt3DSRenderer::IsGlEsContext(qt3ds::render::NVRenderContextType inContextType) + { + qt3ds::render::NVRenderContextType esContextTypes(NVRenderContextValues::GLES2 + | NVRenderContextValues::GLES3 + | NVRenderContextValues::GLES3PLUS); + + if ((inContextType & esContextTypes)) + return true; + + return false; + } + + bool IQt3DSRenderer::IsGlEs3Context(qt3ds::render::NVRenderContextType inContextType) + { + if (inContextType == NVRenderContextValues::GLES3 + || inContextType == NVRenderContextValues::GLES3PLUS) + return true; + + return false; + } + + bool IQt3DSRenderer::IsGl2Context(qt3ds::render::NVRenderContextType inContextType) + { + if (inContextType == NVRenderContextValues::GL2) + return true; + + return false; + } + + IQt3DSRenderer &IQt3DSRenderer::CreateRenderer(IQt3DSRenderContext &inContext) + { + return *QT3DS_NEW(inContext.GetAllocator(), Qt3DSRendererImpl)(inContext); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h new file mode 100644 index 0000000..906afd6 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h @@ -0,0 +1,549 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_GENERATOR_IMPL_H +#define QT3DS_RENDER_SHADER_GENERATOR_IMPL_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderableObjects.h" +#include "Qt3DSRendererImplShaders.h" +#include "Qt3DSRendererImplLayerRenderData.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRenderMesh.h" +#include "Qt3DSRenderModel.h" +#include "foundation/Qt3DSBounds3.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSInvasiveSet.h" +#include "EASTL/string.h" +#include "foundation/Qt3DSDataRef.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderRay.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderWidgets.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "Qt3DSRenderClippingFrustum.h" +#include "foundation/Qt3DSUnionCast.h" +#include "foundation/FastAllocator.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderProfiler.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +namespace qt3ds { +namespace render { + + inline bool FloatLessThan(QT3DSF32 lhs, QT3DSF32 rhs) + { + QT3DSF32 diff = lhs - rhs; + if (fabs(diff) < .001) + return false; + return diff < 0.0f ? true : false; + } + inline bool ISRenderObjectPtrLessThan(const SRenderableObject *lhs, + const SRenderableObject *rhs) + { + return FloatLessThan(lhs->m_CameraDistanceSq, rhs->m_CameraDistanceSq); + } + inline bool ISRenderObjectPtrGreatThan(const SRenderableObject *lhs, + const SRenderableObject *rhs) + { + return FloatLessThan(rhs->m_CameraDistanceSq, lhs->m_CameraDistanceSq); + } + inline bool NonZero(float inValue) { return fabs(inValue) > .001f; } + inline bool NonZero(QT3DSU32 inValue) { return inValue != 0; } + inline bool IsZero(float inValue) { return fabs(inValue) < .001f; } + inline bool IsNotOne(float inValue) { return fabs(1.0f - inValue) > .001f; } + + inline bool IsRectEdgeInBounds(QT3DSI32 inNewRectOffset, QT3DSI32 inNewRectWidth, + QT3DSI32 inCurrentRectOffset, QT3DSI32 inCurrentRectWidth) + { + QT3DSI32 newEnd = inNewRectOffset + inNewRectWidth; + QT3DSI32 currentEnd = inCurrentRectOffset + inCurrentRectWidth; + return inNewRectOffset >= inCurrentRectOffset && newEnd <= currentEnd; + } + + struct STextRenderHelper + { + STextShader *m_Shader; + NVRenderInputAssembler &m_QuadInputAssembler; + STextRenderHelper(STextShader *inShader, NVRenderInputAssembler &inQuadInputAssembler) + : m_Shader(inShader) + , m_QuadInputAssembler(inQuadInputAssembler) + { + } + }; + + struct SPickResultProcessResult : public Qt3DSRenderPickResult + { + SPickResultProcessResult(const Qt3DSRenderPickResult &inSrc) + : Qt3DSRenderPickResult(inSrc) + , m_WasPickConsumed(false) + { + } + SPickResultProcessResult() + : m_WasPickConsumed(false) + { + } + bool m_WasPickConsumed; + }; + + struct STextShaderPtr + { + NVAllocatorCallback &m_Allocator; + bool m_HasGeneratedShader; + STextShader *m_Shader; + STextShaderPtr(NVAllocatorCallback &alloc) + : m_Allocator(alloc) + , m_HasGeneratedShader(false) + , m_Shader(NULL) + { + } + bool HasGeneratedShader() { return m_HasGeneratedShader; } + void Set(STextShader *inShader) + { + m_Shader = inShader; + m_HasGeneratedShader = true; + } + ~STextShaderPtr() + { + if (m_Shader) + NVDelete(m_Allocator, m_Shader); + } + operator STextShader *() { return m_Shader; } + }; + + class QT3DS_AUTOTEST_EXPORT Qt3DSRendererImpl : public IQt3DSRenderer, public IRenderWidgetContext + { + typedef nvhash_map<SShaderDefaultMaterialKey, SShaderGeneratorGeneratedShader *> TShaderMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderConstantBuffer>> + TStrConstanBufMap; + typedef nvhash_map<SRenderInstanceId, NVScopedRefCounted<SLayerRenderData>, + eastl::hash<SRenderInstanceId>> TInstanceRenderMap; + typedef nvvector<SLayerRenderData *> TLayerRenderList; + typedef nvvector<Qt3DSRenderPickResult> TPickResultArray; + + // Items to implement the widget context. + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderVertexBuffer>> + TStrVertBufMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderIndexBuffer>> + TStrIndexBufMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderShaderProgram>> + TStrShaderMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderInputAssembler>> TStrIAMap; + + typedef nvhash_map<long, SNode *> TBoneIdNodeMap; + + IQt3DSRenderContext &m_qt3dsContext; + NVScopedRefCounted<NVRenderContext> m_Context; + NVScopedRefCounted<IBufferManager> m_BufferManager; + NVScopedRefCounted<IOffscreenRenderManager> m_OffscreenRenderManager; + NVScopedRefCounted<IStringTable> m_StringTable; + InvasiveSet<SShaderGeneratorGeneratedShader, SGGSGet, SGGSSet> m_LayerShaders; + // For rendering bounding boxes. + NVScopedRefCounted<NVRenderVertexBuffer> m_BoxVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_BoxIndexBuffer; + NVScopedRefCounted<NVRenderShaderProgram> m_BoxShader; + NVScopedRefCounted<NVRenderShaderProgram> m_ScreenRectShader; + + NVScopedRefCounted<NVRenderVertexBuffer> m_AxisVertexBuffer; + NVScopedRefCounted<NVRenderShaderProgram> m_AxisShader; + + // X,Y quad, broken down into 2 triangles and normalized over + //-1,1. + NVScopedRefCounted<NVRenderVertexBuffer> m_QuadVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_QuadIndexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_RectIndexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_QuadInputAssembler; + NVScopedRefCounted<NVRenderInputAssembler> m_RectInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_QuadAttribLayout; + NVScopedRefCounted<NVRenderAttribLayout> m_RectAttribLayout; + + // X,Y triangle strip quads in screen coord dynamiclly setup + NVScopedRefCounted<NVRenderVertexBuffer> m_QuadStripVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_QuadStripInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_QuadStripAttribLayout; + + // X,Y,Z point which is used for instanced based rendering of points + NVScopedRefCounted<NVRenderVertexBuffer> m_PointVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_PointInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_PointAttribLayout; + + Option<NVScopedRefCounted<SLayerSceneShader>> m_SceneLayerShader; + Option<NVScopedRefCounted<SLayerProgAABlendShader>> m_LayerProgAAShader; + + TShaderMap m_Shaders; + TStrConstanBufMap m_ConstantBuffers; ///< store the the shader constant buffers + // Option is true if we have attempted to generate the shader. + // This does not mean we were successul, however. + Option<NVScopedRefCounted<SDefaultMaterialRenderableDepthShader>> + m_DefaultMaterialDepthPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthPrepassShaderDisplaced; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessLinearPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_DepthTessLinearPrepassShaderDisplaced; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessPhongPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessNPatchPrepassShader; + Option<NVScopedRefCounted<STextDepthShader>> m_TextDepthPrepassShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_DefaultAoPassShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_FakeDepthShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_FakeCubemapDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessNPatchShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessNPatchShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_OrthographicDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessNPatchShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_CubeShadowBlurXShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_CubeShadowBlurYShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_OrthoShadowBlurXShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_OrthoShadowBlurYShader; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeOverlayBlendShader; + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeColorBurnBlendShader; + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeColorDodgeBlendShader; +#endif + // Text shaders may be generated on demand. + STextShaderPtr m_TextShader; + STextShaderPtr m_TextPathShader; + STextShaderPtr m_TextWidgetShader; + STextShaderPtr m_TextOnscreenShader; + + // Overlay used to render all widgets. + NVRenderRect m_BeginFrameViewport; + NVScopedRefCounted<NVRenderTexture2D> m_WidgetTexture; + NVScopedRefCounted<NVRenderFrameBuffer> m_WidgetFBO; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + // Advanced blend mode SW fallback + CResourceTexture2D m_LayerBlendTexture; + NVScopedRefCounted<NVRenderFrameBuffer> m_BlendFB; +#endif + // Allocator for temporary data that is cleared after every layer. + TInstanceRenderMap m_InstanceRenderMap; + TLayerRenderList m_LastFrameLayers; + volatile QT3DSI32 mRefCount; + + // Set from the first layer. + TPickResultArray m_LastPickResults; + + // Temporary information stored only when rendering a particular layer. + SLayerRenderData *m_CurrentLayer; + QT3DSMat44 m_ViewProjection; + eastl::string m_GeneratedShaderString; + + TStrVertBufMap m_WidgetVertexBuffers; + TStrIndexBufMap m_WidgetIndexBuffers; + TStrShaderMap m_WidgetShaders; + TStrIAMap m_WidgetInputAssembler; + + TBoneIdNodeMap m_BoneIdNodeMap; + + bool m_PickRenderPlugins; + bool m_LayerCachingEnabled; + bool m_LayerGPuProfilingEnabled; + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + + public: + Qt3DSRendererImpl(IQt3DSRenderContext &ctx); + virtual ~Qt3DSRendererImpl(); + SShaderDefaultMaterialKeyProperties &DefaultMaterialShaderKeyProperties() + { + return m_DefaultMaterialShaderKeyProperties; + } + + // NVRefCounted + void addRef() override; + void release() override; + + void EnableLayerCaching(bool inEnabled) override { m_LayerCachingEnabled = inEnabled; } + bool IsLayerCachingEnabled() const override { return m_LayerCachingEnabled; } + + void EnableLayerGpuProfiling(bool inEnabled) override; + bool IsLayerGpuProfilingEnabled() const override { return m_LayerGPuProfilingEnabled; } + + // Calls prepare layer for render + // and then do render layer. + bool PrepareLayerForRender(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool inRenderSiblings, const SRenderInstanceId id) override; + void RenderLayer(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool clear, QT3DSVec4 clearColor, bool inRenderSiblings, + const SRenderInstanceId id) override; + void ChildrenUpdated(SNode &inParent) override; + QT3DSF32 GetTextScale(const SText &inText) override; + + SCamera *GetCameraForNode(const SNode &inNode) const override; + Option<SCuboidRect> GetCameraBounds(const SGraphObject &inObject) override; + virtual SLayer *GetLayerForNode(const SNode &inNode) const; + SLayerRenderData *GetOrCreateLayerRenderDataForNode(const SNode &inNode, + const SRenderInstanceId id = nullptr); + + IRenderWidgetContext &GetRenderWidgetContext() + { + return *this; + } + + void BeginFrame() override; + void EndFrame() override; + + void PickRenderPlugins(bool inPick) override { m_PickRenderPlugins = inPick; } + Qt3DSRenderPickResult Pick(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickSiblings, + bool inPickEverything, + const SRenderInstanceId id) override; + + virtual Option<QT3DSVec2> + FacePosition(SNode &inNode, NVBounds3 inBounds, const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, const QT3DSVec2 &inMouseCoords, + NVDataRef<SGraphObject *> inMapperObjects, SBasisPlanes::Enum inPlane) override; + + virtual Qt3DSRenderPickResult PickOffscreenLayer(SLayer &inLayer, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + bool inPickEverything); + + QT3DSVec3 UnprojectToPosition(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec2 &inMouseVec) const override; + QT3DSVec3 UnprojectWithDepth(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec3 &inMouseVec) const override; + QT3DSVec3 ProjectPosition(SNode &inNode, const QT3DSVec3 &inPosition) const override; + + Option<SLayerPickSetup> GetLayerPickSetup(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QSize &inPickDims) override; + + Option<NVRenderRectF> GetLayerRect(SLayer &inLayer) override; + + void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) override; + + // Note that this allocator is completely reset on BeginFrame. + NVAllocatorCallback &GetPerFrameAllocator() override + { + return m_qt3dsContext.GetPerFrameAllocator(); + } + void RenderLayerRect(SLayer &inLayer, const QT3DSVec3 &inColor) override; + void AddRenderWidget(IRenderWidget &inWidget) override; + + SScaleAndPosition GetWorldToPixelScaleFactor(SLayer &inLayer, + const QT3DSVec3 &inWorldPoint) override; + SScaleAndPosition GetWorldToPixelScaleFactor(const SCamera &inCamera, + const QT3DSVec3 &inWorldPoint, + SLayerRenderData &inRenderData); + + void ReleaseLayerRenderResources(SLayer &inLayer, const SRenderInstanceId id) override; + + void RenderQuad(const QT3DSVec2 inDimensions, const QT3DSMat44 &inMVP, + NVRenderTexture2D &inQuadTexture) override; + void RenderQuad() override; + + void RenderPointsIndirect() override; + + // render a screen aligned 2D text + void RenderText2D(QT3DSF32 x, QT3DSF32 y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) override; + bool PrepareTextureAtlasForRender(); + + // render Gpu profiler values + void RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) override; + + // Callback during the layer render process. + void LayerNeedsFrameClear(SLayerRenderData &inLayer); + void BeginLayerDepthPassRender(SLayerRenderData &inLayer); + void EndLayerDepthPassRender(); + void BeginLayerRender(SLayerRenderData &inLayer); + void EndLayerRender(); + void PrepareImageForIbl(SImage &inImage); + + NVRenderShaderProgram *CompileShader(CRegisteredString inName, const char8_t *inVert, + const char8_t *inFrame); + + NVRenderShaderProgram *GenerateShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet); + SShaderGeneratorGeneratedShader *GetShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet); + + SDefaultAoPassShader *GetDefaultAoPassShader(TShaderFeatureSet inFeatureSet); + SDefaultAoPassShader *GetFakeDepthShader(TShaderFeatureSet inFeatureSet); + SDefaultAoPassShader *GetFakeCubeDepthShader(TShaderFeatureSet inFeatureSet); + SDefaultMaterialRenderableDepthShader *GetRenderableDepthShader(); + + SRenderableDepthPrepassShader *GetParaboloidDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetParaboloidDepthNoTessShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessNPatchShader(); + SRenderableDepthPrepassShader *GetCubeShadowDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetCubeDepthNoTessShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessNPatchShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetOrthographicDepthNoTessShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessNPatchShader(); + + SRenderableDepthPrepassShader *GetDepthPrepassShader(bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessPrepassShader(TessModeValues::Enum inTessMode, + bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessLinearPrepassShader(bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessPhongPrepassShader(); + SRenderableDepthPrepassShader *GetDepthTessNPatchPrepassShader(); + STextDepthShader *GetTextDepthShader(); + STextRenderHelper GetShader(STextRenderable &inRenderable, bool inUsePathRendering); + STextRenderHelper GetTextShader(bool inUsePathRendering); + STextRenderHelper GetTextWidgetShader(); + STextRenderHelper GetOnscreenTextShader(); + SLayerSceneShader *GetSceneLayerShader(); + NVRenderShaderProgram *GetTextAtlasEntryShader(); + void GenerateXYQuad(); + void GenerateXYQuadStrip(); + void GenerateXYZPoint(); + eastl::pair<NVRenderVertexBuffer *, NVRenderIndexBuffer *> GetXYQuad(); + SLayerProgAABlendShader *GetLayerProgAABlendShader(); + SShadowmapPreblurShader *GetCubeShadowBlurXShader(); + SShadowmapPreblurShader *GetCubeShadowBlurYShader(); + SShadowmapPreblurShader *GetOrthoShadowBlurXShader(); + SShadowmapPreblurShader *GetOrthoShadowBlurYShader(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + SAdvancedModeBlendShader *GetAdvancedBlendModeShader(AdvancedBlendModes::Enum blendMode); + SAdvancedModeBlendShader *GetOverlayBlendModeShader(); + SAdvancedModeBlendShader *GetColorBurnBlendModeShader(); + SAdvancedModeBlendShader *GetColorDodgeBlendModeShader(); +#endif + SLayerRenderData *GetLayerRenderData() { return m_CurrentLayer; } + SLayerGlobalRenderProperties GetLayerGlobalRenderProperties(); + void UpdateCbAoShadow(const SLayer *pLayer, const SCamera *pCamera, + CResourceTexture2D &inDepthTexture); + + NVRenderContext &GetContext() { return *m_Context; } + + IQt3DSRenderContext &GetQt3DSContext() { return m_qt3dsContext; } + + void DrawScreenRect(NVRenderRectF inRect, const QT3DSVec3 &inColor); + // Binds an offscreen texture. Widgets are rendered last. + void SetupWidgetLayer(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + NVScopedRefCounted<NVRenderTexture2D> GetLayerBlendTexture() + { + return m_LayerBlendTexture.GetTexture(); + } + + NVScopedRefCounted<NVRenderFrameBuffer> GetBlendFB() + { + return m_BlendFB; + } +#endif + // widget context implementation + virtual NVRenderVertexBuffer & + GetOrCreateVertexBuffer(CRegisteredString &inStr, QT3DSU32 stride, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) override; + virtual NVRenderIndexBuffer & + GetOrCreateIndexBuffer(CRegisteredString &inStr, + qt3ds::render::NVRenderComponentTypes::Enum componentType, size_t size, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) override; + virtual NVRenderAttribLayout & + CreateAttributeLayout(NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> attribs) override; + virtual NVRenderInputAssembler & + GetOrCreateInputAssembler(CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef<NVRenderVertexBuffer *> buffers, + const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef<QT3DSU32> strides, NVConstDataRef<QT3DSU32> offsets) override; + + NVRenderVertexBuffer *GetVertexBuffer(CRegisteredString &inStr) override; + NVRenderIndexBuffer *GetIndexBuffer(CRegisteredString &inStr) override; + NVRenderInputAssembler *GetInputAssembler(CRegisteredString &inStr) override; + + NVRenderShaderProgram *GetShader(CRegisteredString inStr) override; + NVRenderShaderProgram *CompileAndStoreShader(CRegisteredString inStr) override; + IShaderProgramGenerator &GetProgramGenerator() override; + + STextDimensions MeasureText(const STextRenderInfo &inText) override; + void RenderText(const STextRenderInfo &inText, const QT3DSVec3 &inTextColor, + const QT3DSVec3 &inBackgroundColor, const QT3DSMat44 &inMVP) override; + + // Given a node and a point in the node's local space (most likely its pivot point), we + // return + // a normal matrix so you can get the axis out, a transformation from node to camera + // a new position and a floating point scale factor so you can render in 1/2 perspective + // mode + // or orthographic mode if you would like to. + virtual SWidgetRenderInformation + GetWidgetRenderInformation(SNode &inNode, const QT3DSVec3 &inPos, + RenderWidgetModes::Enum inWidgetMode) override; + + Option<QT3DSVec2> GetLayerMouseCoords(SLayer &inLayer, const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect = false) const override; + + protected: + Option<QT3DSVec2> GetLayerMouseCoords(SLayerRenderData &inLayer, const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect = false) const; + SPickResultProcessResult ProcessPickResultList(bool inPickEverything); + // If the mouse y coordinates need to be flipped we expect that to happen before entry into + // this function + void GetLayerHitObjectList(SLayerRenderData &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickEverything, + TPickResultArray &outIntersectionResult, + NVAllocatorCallback &inTempAllocator); + void IntersectRayWithSubsetRenderable(const SRay &inRay, + SRenderableObject &inRenderableObject, + TPickResultArray &outIntersectionResultList, + NVAllocatorCallback &inTempAllocator); + }; +} +} + +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp new file mode 100644 index 0000000..362a602 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp @@ -0,0 +1,2220 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderEffect.h" +#include "EASTL/sort.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderPresentation.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderResourceManager.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderEffectSystem.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderResourceBufferObjects.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRendererUtil.h" + +#ifdef WIN32 +#pragma warning(disable : 4355) +#endif + +#define QT3DS_CACHED_POST_EFFECT +const float QT3DS_DEGREES_TO_RADIANS = 0.0174532925199f; + +namespace qt3ds { +namespace render { + using eastl::reverse; + using eastl::stable_sort; + using qt3ds::render::NVRenderContextScopedProperty; + using qt3ds::QT3DSVec2; + + SLayerRenderData::SLayerRenderData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer) + : SLayerRenderPreparationData(inLayer, inRenderer) + , m_LayerTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_TemporalAATexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerPrepassDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerWidgetTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerSsaoTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisampleTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisamplePrepassDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisampleWidgetTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerCachedTexture(NULL) + , m_AdvancedBlendDrawTexture(NULL) + , m_AdvancedBlendBlendTexture(NULL) + , m_AdvancedModeDrawFB(NULL) + , m_AdvancedModeBlendFB(NULL) + , m_ProgressiveAAPassIndex(0) + , m_TemporalAAPassIndex(0) + , m_NonDirtyTemporalAAPassIndex(0) + , m_TextScale(1.0f) + , mRefCount(0) + , m_DepthBufferFormat(NVRenderTextureFormats::Unknown) + { + } + + SLayerRenderData::~SLayerRenderData() + { + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) + theResourceManager.Release(*m_LayerCachedTexture); + if (m_AdvancedModeDrawFB) { + m_AdvancedModeDrawFB->release(); + m_AdvancedModeDrawFB = NULL; + } + if (m_AdvancedModeBlendFB) { + m_AdvancedModeBlendFB->release(); + m_AdvancedModeBlendFB = NULL; + } + if (m_AdvancedBlendBlendTexture) + m_AdvancedBlendBlendTexture = NULL; + if (m_AdvancedBlendDrawTexture) + m_AdvancedBlendDrawTexture = NULL; + } + void SLayerRenderData::PrepareForRender(const QSize &inViewportDimensions) + { + SLayerRenderPreparationData::PrepareForRender(inViewportDimensions); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // at that time all values shoud be updated + m_Renderer.UpdateCbAoShadow(&m_Layer, m_Camera, m_LayerDepthTexture); + + // Generate all necessary lighting keys + + if (thePrepResult.m_Flags.WasLayerDataDirty()) { + m_ProgressiveAAPassIndex = 0; + } + + // Get rid of the layer texture if we aren't rendering to texture this frame. + if (m_LayerTexture && !thePrepResult.m_Flags.ShouldRenderToTexture()) { + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) { + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + + m_LayerTexture.ReleaseTexture(); + m_LayerDepthTexture.ReleaseTexture(); + m_LayerWidgetTexture.ReleaseTexture(); + m_LayerSsaoTexture.ReleaseTexture(); + m_LayerMultisampleTexture.ReleaseTexture(); + m_LayerMultisamplePrepassDepthTexture.ReleaseTexture(); + m_LayerMultisampleWidgetTexture.ReleaseTexture(); + } + + if (NeedsWidgetTexture() == false) + m_LayerWidgetTexture.ReleaseTexture(); + + if (m_LayerDepthTexture && !thePrepResult.m_Flags.RequiresDepthTexture()) + m_LayerDepthTexture.ReleaseTexture(); + + if (m_LayerSsaoTexture && !thePrepResult.m_Flags.RequiresSsaoPass()) + m_LayerSsaoTexture.ReleaseTexture(); + + m_Renderer.LayerNeedsFrameClear(*this); + + // Clean up the texture cache if layer dimensions changed + if (inViewportDimensions.width() != m_previousDimensions.width() + || inViewportDimensions.height() != m_previousDimensions.height()) { + m_LayerTexture.ReleaseTexture(); + m_LayerDepthTexture.ReleaseTexture(); + m_LayerSsaoTexture.ReleaseTexture(); + m_LayerWidgetTexture.ReleaseTexture(); + m_LayerPrepassDepthTexture.ReleaseTexture(); + m_TemporalAATexture.ReleaseTexture(); + m_LayerMultisampleTexture.ReleaseTexture(); + m_LayerMultisamplePrepassDepthTexture.ReleaseTexture(); + m_LayerMultisampleWidgetTexture.ReleaseTexture(); + + m_previousDimensions.setWidth(inViewportDimensions.width()); + m_previousDimensions.setHeight(inViewportDimensions.height()); + + theResourceManager.DestroyFreeSizedResources(); + + // Effect system uses different resource manager, so clean that up too + m_Renderer.GetQt3DSContext().GetEffectSystem().GetResourceManager() + .DestroyFreeSizedResources(); + } + } + + NVRenderTextureFormats::Enum SLayerRenderData::GetDepthBufferFormat() + { + if (m_DepthBufferFormat == NVRenderTextureFormats::Unknown) { + QT3DSU32 theExistingDepthBits = m_Renderer.GetContext().GetDepthBits(); + QT3DSU32 theExistingStencilBits = m_Renderer.GetContext().GetStencilBits(); + switch (theExistingDepthBits) { + case 32: + m_DepthBufferFormat = NVRenderTextureFormats::Depth32; + break; + case 24: + // check if we have stencil bits + if (theExistingStencilBits > 0) + m_DepthBufferFormat = + NVRenderTextureFormats::Depth24Stencil8; // currently no stencil usage + // should be Depth24Stencil8 in + // this case + else + m_DepthBufferFormat = NVRenderTextureFormats::Depth24; + break; + case 16: + m_DepthBufferFormat = NVRenderTextureFormats::Depth16; + break; + default: + QT3DS_ASSERT(false); + m_DepthBufferFormat = NVRenderTextureFormats::Depth16; + break; + } + } + return m_DepthBufferFormat; + } + + NVRenderFrameBufferAttachments::Enum + SLayerRenderData::GetFramebufferDepthAttachmentFormat(NVRenderTextureFormats::Enum depthFormat) + { + NVRenderFrameBufferAttachments::Enum fmt = NVRenderFrameBufferAttachments::Depth; + + switch (depthFormat) { + case NVRenderTextureFormats::Depth16: + case NVRenderTextureFormats::Depth24: + case NVRenderTextureFormats::Depth32: + fmt = NVRenderFrameBufferAttachments::Depth; + break; + case NVRenderTextureFormats::Depth24Stencil8: + fmt = NVRenderFrameBufferAttachments::DepthStencil; + break; + default: + QT3DS_ASSERT(false); + break; + } + + return fmt; + } + + void SLayerRenderData::RenderAoPass() + { + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theContext(m_Renderer.GetContext()); + SDefaultAoPassShader *shader = m_Renderer.GetDefaultAoPassShader(GetShaderFeatureSet()); + if (shader == NULL) + return; + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetActiveShader(&(shader->m_Shader)); + + // Setup constants + shader->m_CameraDirection.Set(m_CameraDirection); + shader->m_ViewMatrix.Set(m_Camera->m_GlobalTransform); + + shader->m_DepthTexture.Set(m_LayerDepthTexture); + shader->m_DepthSamplerSize.Set(QT3DSVec2(m_LayerDepthTexture->GetTextureDetails().m_Width, + m_LayerDepthTexture->GetTextureDetails().m_Height)); + + // Important uniforms for AO calculations + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + shader->m_CameraProperties.Set(theCameraProps); + shader->m_AoShadowParams.Set(); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + m_Renderer.EndLayerDepthPassRender(); + } + + void SLayerRenderData::RenderFakeDepthMapPass(NVRenderTexture2D *theDepthTex, + NVRenderTextureCube *theDepthCube) + { + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theContext(m_Renderer.GetContext()); + SDefaultAoPassShader *shader = theDepthTex + ? m_Renderer.GetFakeDepthShader(GetShaderFeatureSet()) + : m_Renderer.GetFakeCubeDepthShader(GetShaderFeatureSet()); + if (shader == NULL) + return; + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetActiveShader(&(shader->m_Shader)); + + // Setup constants + shader->m_CameraDirection.Set(m_CameraDirection); + shader->m_ViewMatrix.Set(m_Camera->m_GlobalTransform); + + shader->m_DepthTexture.Set(theDepthTex); + shader->m_CubeTexture.Set(theDepthCube); + shader->m_DepthSamplerSize.Set(QT3DSVec2(theDepthTex->GetTextureDetails().m_Width, + theDepthTex->GetTextureDetails().m_Height)); + + // Important uniforms for AO calculations + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + shader->m_CameraProperties.Set(theCameraProps); + shader->m_AoShadowParams.Set(); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + } + + namespace { + + void computeFrustumBounds(const SCamera &inCamera, const NVRenderRectF &inViewPort, + QT3DSVec3 &ctrBound, QT3DSVec3 camVerts[8]) + { + QT3DSVec3 camEdges[4]; + + const QT3DSF32 *dataPtr(inCamera.m_GlobalTransform.front()); + QT3DSVec3 camX(dataPtr[0], dataPtr[1], dataPtr[2]); + QT3DSVec3 camY(dataPtr[4], dataPtr[5], dataPtr[6]); + QT3DSVec3 camZ(dataPtr[8], dataPtr[9], dataPtr[10]); + + float tanFOV = tanf(inCamera.verticalFov(inViewPort) * 0.5f); + float asTanFOV = tanFOV * inViewPort.m_Width / inViewPort.m_Height; + camEdges[0] = -asTanFOV * camX + tanFOV * camY + camZ; + camEdges[1] = asTanFOV * camX + tanFOV * camY + camZ; + camEdges[2] = asTanFOV * camX - tanFOV * camY + camZ; + camEdges[3] = -asTanFOV * camX - tanFOV * camY + camZ; + + for (int i = 0; i < 4; ++i) { + camEdges[i].x = -camEdges[i].x; + camEdges[i].y = -camEdges[i].y; + } + + camVerts[0] = inCamera.m_Position + camEdges[0] * inCamera.m_ClipNear; + camVerts[1] = inCamera.m_Position + camEdges[0] * inCamera.m_ClipFar; + camVerts[2] = inCamera.m_Position + camEdges[1] * inCamera.m_ClipNear; + camVerts[3] = inCamera.m_Position + camEdges[1] * inCamera.m_ClipFar; + camVerts[4] = inCamera.m_Position + camEdges[2] * inCamera.m_ClipNear; + camVerts[5] = inCamera.m_Position + camEdges[2] * inCamera.m_ClipFar; + camVerts[6] = inCamera.m_Position + camEdges[3] * inCamera.m_ClipNear; + camVerts[7] = inCamera.m_Position + camEdges[3] * inCamera.m_ClipFar; + + ctrBound = camVerts[0]; + for (int i = 1; i < 8; ++i) { + ctrBound += camVerts[i]; + } + ctrBound *= 0.125f; + } + + void SetupCameraForShadowMap(const QT3DSVec2 &inCameraVec, NVRenderContext & /*inContext*/, + const NVRenderRectF &inViewport, const SCamera &inCamera, + const SLight *inLight, SCamera &theCamera) + { + // setup light matrix + QT3DSU32 mapRes = 1 << inLight->m_ShadowMapRes; + NVRenderRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes); + theCamera.m_ClipNear = 1.0f; + theCamera.m_ClipFar = inLight->m_ShadowMapFar; + // Setup camera projection + QT3DSVec3 inLightPos = inLight->GetGlobalPos(); + QT3DSVec3 inLightDir = inLight->GetDirection(); + + if (inLight->m_Flags.IsLeftHanded()) + inLightPos.z = -inLightPos.z; + + inLightPos -= inLightDir * inCamera.m_ClipNear; + theCamera.m_FOV = inLight->m_ShadowMapFov * QT3DS_DEGREES_TO_RADIANS; + + if (inLight->m_LightType == RenderLightTypes::Directional) { + QT3DSVec3 frustBounds[8], boundCtr; + computeFrustumBounds(inCamera, inViewport, boundCtr, frustBounds); + + QT3DSVec3 forward = inLightDir; + forward.normalize(); + QT3DSVec3 right = forward.cross(QT3DSVec3(0, 1, 0)); + right.normalize(); + QT3DSVec3 up = right.cross(forward); + up.normalize(); + + // Calculate bounding box of the scene camera frustum + float minDistanceZ = std::numeric_limits<float>::max(); + float maxDistanceZ = -std::numeric_limits<float>::max(); + float minDistanceY = std::numeric_limits<float>::max(); + float maxDistanceY = -std::numeric_limits<float>::max(); + float minDistanceX = std::numeric_limits<float>::max(); + float maxDistanceX = -std::numeric_limits<float>::max(); + for (int i = 0; i < 8; ++i) { + float distanceZ = frustBounds[i].dot(forward); + if (distanceZ < minDistanceZ) + minDistanceZ = distanceZ; + if (distanceZ > maxDistanceZ) + maxDistanceZ = distanceZ; + float distanceY = frustBounds[i].dot(up); + if (distanceY < minDistanceY) + minDistanceY = distanceY; + if (distanceY > maxDistanceY) + maxDistanceY = distanceY; + float distanceX = frustBounds[i].dot(right); + if (distanceX < minDistanceX) + minDistanceX = distanceX; + if (distanceX > maxDistanceX) + maxDistanceX = distanceX; + } + + // Apply bounding box parameters to shadow map camera projection matrix + // so that the whole scene is fit inside the shadow map + inLightPos = boundCtr; + theViewport.m_Height = abs(maxDistanceY - minDistanceY); + theViewport.m_Width = abs(maxDistanceX - minDistanceX); + theCamera.m_ClipNear = -abs(maxDistanceZ - minDistanceZ); + theCamera.m_ClipFar = abs(maxDistanceZ - minDistanceZ); + } + + theCamera.m_Flags.SetLeftHanded(false); + + theCamera.m_Flags.ClearOrSet(inLight->m_LightType == RenderLightTypes::Directional, + NodeFlagValues::Orthographic); + theCamera.m_Parent = NULL; + theCamera.m_Pivot = inLight->m_Pivot; + + if (inLight->m_LightType != RenderLightTypes::Point) { + theCamera.LookAt(inLightPos, QT3DSVec3(0, 1.0, 0), inLightPos + inLightDir); + } else { + theCamera.LookAt(inLightPos, QT3DSVec3(0, 1.0, 0), QT3DSVec3(0, 0, 0)); + } + + theCamera.CalculateGlobalVariables(theViewport, + QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + } + } + + void SetupCubeShadowCameras(const SLight *inLight, SCamera inCameras[6]) + { + // setup light matrix + QT3DSU32 mapRes = 1 << inLight->m_ShadowMapRes; + NVRenderRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes); + QT3DSVec3 rotOfs[6]; + + QT3DS_ASSERT(inLight != NULL); + QT3DS_ASSERT(inLight->m_LightType != RenderLightTypes::Directional); + + QT3DSVec3 inLightPos = inLight->GetGlobalPos(); + if (inLight->m_Flags.IsLeftHanded()) + inLightPos.z = -inLightPos.z; + + rotOfs[0] = QT3DSVec3(0.f, -NVHalfPi, NVPi); + rotOfs[1] = QT3DSVec3(0.f, NVHalfPi, NVPi); + rotOfs[2] = QT3DSVec3(NVHalfPi, 0.f, 0.f); + rotOfs[3] = QT3DSVec3(-NVHalfPi, 0.f, 0.f); + rotOfs[4] = QT3DSVec3(0.f, NVPi, -NVPi); + rotOfs[5] = QT3DSVec3(0.f, 0.f, NVPi); + + for (int i = 0; i < 6; ++i) { + inCameras[i].m_Flags.SetLeftHanded(false); + + inCameras[i].m_Flags.ClearOrSet(false, NodeFlagValues::Orthographic); + inCameras[i].m_Parent = NULL; + inCameras[i].m_Pivot = inLight->m_Pivot; + inCameras[i].m_ClipNear = 1.0f; + inCameras[i].m_ClipFar = NVMax<QT3DSF32>(2.0f, inLight->m_ShadowMapFar); + inCameras[i].m_FOV = inLight->m_ShadowMapFov * QT3DS_DEGREES_TO_RADIANS; + + inCameras[i].m_Position = inLightPos; + inCameras[i].m_Rotation = rotOfs[i]; + inCameras[i].CalculateGlobalVariables( + theViewport, QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + } + + /* + if ( inLight->m_LightType == RenderLightTypes::Point ) return; + + QT3DSVec3 viewDirs[6]; + QT3DSVec3 viewUp[6]; + QT3DSMat33 theDirMatrix( inLight->m_GlobalTransform.getUpper3x3() ); + + viewDirs[0] = theDirMatrix.transform( QT3DSVec3( 1.f, 0.f, 0.f ) ); + viewDirs[2] = theDirMatrix.transform( QT3DSVec3( 0.f, -1.f, 0.f ) ); + viewDirs[4] = theDirMatrix.transform( QT3DSVec3( 0.f, 0.f, 1.f ) ); + viewDirs[0].normalize(); viewDirs[2].normalize(); viewDirs[4].normalize(); + viewDirs[1] = -viewDirs[0]; + viewDirs[3] = -viewDirs[2]; + viewDirs[5] = -viewDirs[4]; + + viewUp[0] = viewDirs[2]; + viewUp[1] = viewDirs[2]; + viewUp[2] = viewDirs[5]; + viewUp[3] = viewDirs[4]; + viewUp[4] = viewDirs[2]; + viewUp[5] = viewDirs[2]; + + for (int i = 0; i < 6; ++i) + { + inCameras[i].LookAt( inLightPos, viewUp[i], inLightPos + viewDirs[i] ); + inCameras[i].CalculateGlobalVariables( theViewport, QT3DSVec2( theViewport.m_Width, + theViewport.m_Height ) ); + } + */ + } + + inline void RenderRenderableShadowMapPass(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet, + QT3DSU32 lightIndex, const SCamera &inCamera) + { + if (!inObject.m_RenderableFlags.IsShadowCaster()) + return; + + SShadowMapEntry *pEntry = inData.m_ShadowMapManager->GetShadowMapEntry(lightIndex); + + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + static_cast<SSubsetRenderableBase &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + static_cast<SSubsetRenderableBase &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + } else if (inObject.m_RenderableFlags.IsPath()) { + static_cast<SPathRenderable &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + } + } + + void SLayerRenderData::RenderShadowCubeBlurPass(CResourceFrameBuffer *theFB, + NVRenderTextureCube *target0, + NVRenderTextureCube *target1, QT3DSF32 filterSz, + QT3DSF32 clipFar) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + + SShadowmapPreblurShader *shaderX = m_Renderer.GetCubeShadowBlurXShader(); + SShadowmapPreblurShader *shaderY = m_Renderer.GetCubeShadowBlurYShader(); + + if (shaderX == NULL) + return; + if (shaderY == NULL) + return; + // if ( theShader == NULL ) return; + + // Enable drawing to 6 color attachment buffers for cubemap passes + qt3ds::QT3DSI32 buffers[6] = { 0, 1, 2, 3, 4, 5 }; + qt3ds::foundation::NVConstDataRef<qt3ds::QT3DSI32> bufferList(buffers, 6); + theContext.SetDrawBuffers(bufferList); + + // Attach framebuffer targets + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, *target1, + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, *target1, + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, *target1, + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, *target1, + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, *target1, + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, *target1, + NVRenderTextureCubeFaces::CubeNegZ); + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + // theContext.SetColorWritesEnabled(true); + theContext.SetActiveShader(&(shaderX->m_Shader)); + + shaderX->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderX->m_DepthCube.Set(target0); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetActiveShader(&(shaderY->m_Shader)); + + // Lather, Rinse, and Repeat for the Y-blur pass + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, *target0, + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, *target0, + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, *target0, + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, *target0, + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, *target0, + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, *target0, + NVRenderTextureCubeFaces::CubeNegZ); + + shaderY->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderY->m_DepthCube.Set(target1); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetDepthWriteEnabled(true); + theContext.SetDepthTestEnabled(true); + // theContext.SetColorWritesEnabled(false); + + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegZ); + + theContext.SetDrawBuffers(qt3ds::foundation::toConstDataRef((qt3ds::QT3DSI32)0)); + } + + void SLayerRenderData::RenderShadowMapBlurPass(CResourceFrameBuffer *theFB, + NVRenderTexture2D *target0, + NVRenderTexture2D *target1, QT3DSF32 filterSz, + QT3DSF32 clipFar) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + + SShadowmapPreblurShader *shaderX = m_Renderer.GetOrthoShadowBlurXShader(); + SShadowmapPreblurShader *shaderY = m_Renderer.GetOrthoShadowBlurYShader(); + + if (shaderX == NULL) + return; + if (shaderY == NULL) + return; + + // Attach framebuffer target + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *target1); + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, *target1 ); + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetColorWritesEnabled(true); + theContext.SetActiveShader(&(shaderX->m_Shader)); + + shaderX->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderX->m_DepthMap.Set(target0); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *target0); + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, *target0 ); + theContext.SetActiveShader(&(shaderY->m_Shader)); + + shaderY->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderY->m_DepthMap.Set(target1); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetDepthWriteEnabled(true); + theContext.SetDepthTestEnabled(true); + theContext.SetColorWritesEnabled(false); + + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, + //NVRenderTextureOrRenderBuffer() ); + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer()); + } + + void SLayerRenderData::RenderShadowMapPass(CResourceFrameBuffer *theFB) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::RenderShadowMapPass"); + + if (m_Camera == NULL || !GetShadowMapManager()) + return; + + // Check if we have anything to render + if (m_OpaqueObjects.size() == 0 || m_Lights.size() == 0) + return; + + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + + // we may change the viewport + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theRenderContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + + // disable color writes + // theRenderContext.SetColorWritesEnabled( false ); + theRenderContext.SetColorWritesEnabled(true); + theRenderContext.SetDepthWriteEnabled(true); + theRenderContext.SetCullingEnabled(false); + theRenderContext.SetClearColor(QT3DSVec4(1.0f)); + + // we render the shadow map with a slight offset to prevent shadow acne and cull the front + // faces + NVScopedRefCounted<qt3ds::render::NVRenderRasterizerState> rsdefaultstate = + theRenderContext.CreateRasterizerState(0.0, 0.0, qt3ds::render::NVRenderFaces::Back); + NVScopedRefCounted<qt3ds::render::NVRenderRasterizerState> rsstate = + theRenderContext.CreateRasterizerState(1.5, 2.0, qt3ds::render::NVRenderFaces::Front); + theRenderContext.SetRasterizerState(rsstate); + + qt3ds::render::NVRenderClearFlags clearFlags(qt3ds::render::NVRenderClearValues::Depth + | qt3ds::render::NVRenderClearValues::Stencil + | qt3ds::render::NVRenderClearValues::Color); + + for (QT3DSU32 i = 0; i < m_Lights.size(); i++) { + // don't render shadows when not casting + if (m_Lights[i]->m_CastShadow == false) + continue; + SShadowMapEntry *pEntry = m_ShadowMapManager->GetShadowMapEntry(i); + if (pEntry && pEntry->m_DepthMap && pEntry->m_DepthCopy && pEntry->m_DepthRender) { + SCamera theCamera; + + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + SetupCameraForShadowMap(theCameraProps, m_Renderer.GetContext(), + __viewport.m_InitialValue, *m_Camera, + m_Lights[i], theCamera); + // we need this matrix for the final rendering + theCamera.CalculateViewProjectionMatrix(pEntry->m_LightVP); + pEntry->m_LightView = theCamera.m_GlobalTransform.getInverse(); + + STextureDetails theDetails(pEntry->m_DepthMap->GetTextureDetails()); + theRenderContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *pEntry->m_DepthMap); + (*theFB)->Attach(NVRenderFrameBufferAttachments::DepthStencil, + *pEntry->m_DepthRender); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableShadowMapPass, false, true, true, i, theCamera); + RenderShadowMapBlurPass(theFB, pEntry->m_DepthMap, pEntry->m_DepthCopy, + m_Lights[i]->m_ShadowFilter, m_Lights[i]->m_ShadowMapFar); + } else if (pEntry && pEntry->m_DepthCube && pEntry->m_CubeCopy + && pEntry->m_DepthRender) { + SCamera theCameras[6]; + + SetupCubeShadowCameras(m_Lights[i], theCameras); + + // pEntry->m_LightView = m_Lights[i]->m_LightType == RenderLightTypes::Point ? + // QT3DSMat44::createIdentity() + // : m_Lights[i]->m_GlobalTransform; + pEntry->m_LightView = QT3DSMat44::createIdentity(); + + STextureDetails theDetails(pEntry->m_DepthCube->GetTextureDetails()); + theRenderContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + + // int passes = m_Lights[i]->m_LightType == RenderLightTypes::Point ? 6 : 5; + int passes = 6; + for (int k = 0; k < passes; ++k) { + // theCameras[k].CalculateViewProjectionMatrix( pEntry->m_LightCubeVP[k] ); + pEntry->m_LightCubeView[k] = theCameras[k].m_GlobalTransform.getInverse(); + theCameras[k].CalculateViewProjectionMatrix(pEntry->m_LightVP); + + // Geometry shader multiplication really doesn't work unless you have a + // 6-layered 3D depth texture... + // Otherwise, you have no way to depth test while rendering... + // which more or less completely defeats the purpose of having a cubemap render + // target. + NVRenderTextureCubeFaces::Enum curFace = + (NVRenderTextureCubeFaces::Enum)(k + 1); + //(*theFB)->AttachFace( NVRenderFrameBufferAttachments::DepthStencil, + //*pEntry->m_DepthCube, curFace ); + (*theFB)->Attach(NVRenderFrameBufferAttachments::DepthStencil, + *pEntry->m_DepthRender); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, + *pEntry->m_DepthCube, curFace); + (*theFB)->IsComplete(); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableShadowMapPass, false, true, true, i, + theCameras[k]); + } + + RenderShadowCubeBlurPass(theFB, pEntry->m_DepthCube, pEntry->m_CubeCopy, + m_Lights[i]->m_ShadowFilter, m_Lights[i]->m_ShadowMapFar); + } + } + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Depth, NVRenderTextureOrRenderBuffer()); + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer()); + + // enable color writes + theRenderContext.SetColorWritesEnabled(true); + theRenderContext.SetCullingEnabled(true); + theRenderContext.SetClearColor(QT3DSVec4(0.0f)); + // reset rasterizer state + theRenderContext.SetRasterizerState(rsdefaultstate); + + m_Renderer.EndLayerDepthPassRender(); + } + + inline void RenderRenderableDepthPass(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet, QT3DSU32, + const SCamera &inCamera) + { + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) { + static_cast<SSubsetRenderable &>(inObject).RenderDepthPass(inCameraProps); + } else if (inObject.m_RenderableFlags.IsText()) { + static_cast<STextRenderable &>(inObject).RenderDepthPass(inCameraProps); +#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()) { + static_cast<SPathRenderable &>(inObject).RenderDepthPass( + inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, NULL); + } else { + QT3DS_ASSERT(false); + } + } + + void SLayerRenderData::RenderDepthPass(bool inEnableTransparentDepthWrite) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::RenderDepthPass"); + if (m_Camera == NULL) + return; + + // Avoid running this method if possible. + if ((inEnableTransparentDepthWrite == false + && (m_OpaqueObjects.size() == 0 + || m_Layer.m_Flags.IsLayerEnableDepthPrepass() == false)) + || m_Layer.m_Flags.IsLayerEnableDepthTest() == false) + return; + + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + + // disable color writes + theRenderContext.SetColorWritesEnabled(false); + theRenderContext.SetDepthWriteEnabled(true); + + qt3ds::render::NVRenderClearFlags clearFlags(qt3ds::render::NVRenderClearValues::Stencil + | qt3ds::render::NVRenderClearValues::Depth); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableDepthPass, false, true, inEnableTransparentDepthWrite, 0, + *m_Camera); + + // enable color writes + theRenderContext.SetColorWritesEnabled(true); + + m_Renderer.EndLayerDepthPassRender(); + } + + inline void RenderRenderable(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet inFeatureSet, QT3DSU32, + const SCamera &inCamera) + { + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + 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 = + static_cast<SCustomMaterialRenderable &>(inObject); + if (!inData.m_Layer.m_LightProbe && theObject.m_Material.m_IblProbe) + inData.SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", + theObject.m_Material.m_IblProbe->m_TextureData.m_Texture + != NULL); + else if (inData.m_Layer.m_LightProbe) + inData.SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", + inData.m_Layer.m_LightProbe->m_TextureData.m_Texture + != NULL); + + static_cast<SCustomMaterialRenderable &>(inObject).Render( + inCameraProps, inData, inData.m_Layer, inData.m_Lights, inCamera, + inData.m_LayerDepthTexture, inData.m_LayerSsaoTexture, inFeatureSet); + } else if (inObject.m_RenderableFlags.IsPath()) { + static_cast<SPathRenderable &>(inObject).Render( + inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, + inData.m_LayerDepthTexture, inData.m_LayerSsaoTexture, inFeatureSet); + } else { + QT3DS_ASSERT(false); + } + } + + void SLayerRenderData::RunRenderPass(TRenderRenderableFunction inRenderFn, + bool inEnableBlending, bool inEnableDepthWrite, + bool inEnableTransparentDepthWrite, QT3DSU32 indexLight, + const SCamera &inCamera, CResourceFrameBuffer *theFB) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + theRenderContext.SetDepthFunction(qt3ds::render::NVRenderBoolOp::LessThanOrEqual); + theRenderContext.SetBlendingEnabled(false); + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + NVDataRef<SRenderableObject *> theOpaqueObjects = GetOpaqueRenderableObjects(); + bool usingDepthBuffer = + m_Layer.m_Flags.IsLayerEnableDepthTest() && theOpaqueObjects.size() > 0; + + if (usingDepthBuffer) { + theRenderContext.SetDepthTestEnabled(true); + theRenderContext.SetDepthWriteEnabled(inEnableDepthWrite); + } else { + theRenderContext.SetDepthWriteEnabled(false); + theRenderContext.SetDepthTestEnabled(false); + } + + for (QT3DSU32 idx = 0, end = theOpaqueObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theOpaqueObjects[idx]); + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), indexLight, + inCamera); + } + + // transparent objects + if (inEnableBlending || m_Layer.m_Flags.IsLayerEnableDepthTest() == false) { + theRenderContext.SetBlendingEnabled(true && inEnableBlending); + theRenderContext.SetDepthWriteEnabled(inEnableTransparentDepthWrite); + + NVDataRef<SRenderableObject *> theTransparentObjects = GetTransparentRenderableObjects(); + // Assume all objects have transparency if the layer's depth test enabled flag is true. + if (m_Layer.m_Flags.IsLayerEnableDepthTest() == true) { + for (QT3DSU32 idx = 0, end = theTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theTransparentObjects[idx]); + if (!(theObject.m_RenderableFlags.IsCompletelyTransparent())) { +#ifdef ADVANCED_BLEND_SW_FALLBACK + // SW fallback for advanced blend modes. + // Renders transparent objects to a separate FBO and blends them in shader + // with the opaque items and background. + DefaultMaterialBlendMode::Enum blendMode + = DefaultMaterialBlendMode::Enum::Normal; + if (theObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + blendMode = static_cast<SSubsetRenderable &>(theObject).getBlendingMode(); + bool useBlendFallback = (blendMode == DefaultMaterialBlendMode::Overlay || + blendMode == DefaultMaterialBlendMode::ColorBurn || + blendMode == DefaultMaterialBlendMode::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR() && + m_LayerPrepassDepthTexture; + if (useBlendFallback) + SetupDrawFB(true); +#endif + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, + m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), + indexLight, inCamera); +#ifdef ADVANCED_BLEND_SW_FALLBACK + // SW fallback for advanced blend modes. + // Continue blending after transparent objects have been rendered to a FBO + if (useBlendFallback) { + BlendAdvancedToFB(blendMode, true, theFB); + // restore blending status + theRenderContext.SetBlendingEnabled(inEnableBlending); + // restore depth test status + theRenderContext.SetDepthTestEnabled(usingDepthBuffer); + theRenderContext.SetDepthWriteEnabled(inEnableTransparentDepthWrite); + } +#endif + } + } + } + // If the layer doesn't have depth enabled then we have to render via an alternate route + // where the transparent objects vector could have both opaque and transparent objects. + else { + for (QT3DSU32 idx = 0, end = theTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theTransparentObjects[idx]); + if (!(theObject.m_RenderableFlags.IsCompletelyTransparent())) { +#ifdef ADVANCED_BLEND_SW_FALLBACK + DefaultMaterialBlendMode::Enum blendMode + = DefaultMaterialBlendMode::Enum::Normal; + if (theObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + blendMode = static_cast<SSubsetRenderable &>(theObject).getBlendingMode(); + bool useBlendFallback = (blendMode == DefaultMaterialBlendMode::Overlay || + blendMode == DefaultMaterialBlendMode::ColorBurn || + blendMode == DefaultMaterialBlendMode::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR(); + + if (theObject.m_RenderableFlags.HasTransparency()) { + theRenderContext.SetBlendingEnabled(true && inEnableBlending); + // If we have SW fallback for blend mode, render to a FBO and blend back. + // Slow as this must be done per-object (transparent and opaque items + // are mixed, not batched) + if (useBlendFallback) + SetupDrawFB(false); + } +#endif + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, + m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), + indexLight, inCamera); +#ifdef ADVANCED_BLEND_SW_FALLBACK + if (useBlendFallback) { + BlendAdvancedToFB(blendMode, false, theFB); + // restore blending status + theRenderContext.SetBlendingEnabled(inEnableBlending); + + } +#endif + } + } + } + } + } + + void SLayerRenderData::Render(CResourceFrameBuffer *theFB) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::Render"); + if (m_Camera == NULL) + return; + + m_Renderer.BeginLayerRender(*this); + RunRenderPass(RenderRenderable, true, !m_Layer.m_Flags.IsLayerEnableDepthPrepass(), false, + 0, *m_Camera, theFB); + m_Renderer.EndLayerRender(); + } + + void SLayerRenderData::CreateGpuProfiler() + { + if (m_Renderer.GetContext().IsTimerQuerySupported()) { + m_LayerProfilerGpu = IRenderProfiler::CreateGpuProfiler( + m_Renderer.GetContext().GetFoundation(), m_Renderer.GetQt3DSContext(), + m_Renderer.GetContext()); + } + } + + void SLayerRenderData::StartProfiling(CRegisteredString &nameID, bool sync) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->StartTimer(nameID, false, sync); + } + } + + void SLayerRenderData::EndProfiling(CRegisteredString &nameID) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->EndTimer(nameID); + } + } + + void SLayerRenderData::StartProfiling(const char *nameID, bool sync) + { + if (m_LayerProfilerGpu.mPtr) { + CRegisteredString theStr( + m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(nameID)); + m_LayerProfilerGpu->StartTimer(theStr, false, sync); + } + } + + void SLayerRenderData::EndProfiling(const char *nameID) + { + if (m_LayerProfilerGpu.mPtr) { + CRegisteredString theStr( + m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(nameID)); + m_LayerProfilerGpu->EndTimer(theStr); + } + } + + void SLayerRenderData::AddVertexCount(QT3DSU32 count) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->AddVertexCount(count); + } + } + + // Assumes the viewport is setup appropriately to render the widget. + void SLayerRenderData::RenderRenderWidgets() + { + if (m_Camera) { + NVRenderContext &theContext(m_Renderer.GetContext()); + for (QT3DSU32 idx = 0, end = m_IRenderWidgets.size(); idx < end; ++idx) { + IRenderWidget &theWidget = *m_IRenderWidgets[idx]; + theWidget.Render(m_Renderer, theContext); + } + } + } + +#ifdef ADVANCED_BLEND_SW_FALLBACK + void SLayerRenderData::BlendAdvancedEquationSwFallback(NVRenderTexture2D *drawTexture, + NVRenderTexture2D *layerTexture, + AdvancedBlendModes::Enum blendMode) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + SAdvancedModeBlendShader *shader = m_Renderer.GetAdvancedBlendModeShader(blendMode); + if (shader == NULL) + return; + + theContext.SetActiveShader(&(shader->m_Shader)); + + shader->m_baseLayer.Set(layerTexture); + shader->m_blendLayer.Set(drawTexture); + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + } + + void SLayerRenderData::SetupDrawFB(bool depthEnabled) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + // create drawing FBO and texture, if not existing + if (!m_AdvancedModeDrawFB) + m_AdvancedModeDrawFB = theRenderContext.CreateFrameBuffer(); + if (!m_AdvancedBlendDrawTexture) { + m_AdvancedBlendDrawTexture = theRenderContext.CreateTexture2D(); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + m_AdvancedBlendDrawTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theViewport.m_Width, + theViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_AdvancedModeDrawFB->Attach(NVRenderFrameBufferAttachments::Color0, + *m_AdvancedBlendDrawTexture); + // Use existing depth prepass information when rendering transparent objects to a FBO + if (depthEnabled) + m_AdvancedModeDrawFB->Attach(NVRenderFrameBufferAttachments::Depth, + *m_LayerPrepassDepthTexture); + } + theRenderContext.SetRenderTarget(m_AdvancedModeDrawFB); + // make sure that depth testing is on in order to render just the + // depth-passed objects (=transparent objects) and leave background intact + if (depthEnabled) + theRenderContext.SetDepthTestEnabled(true); + theRenderContext.SetBlendingEnabled(false); + // clear color commonly is the layer background, make sure that it is all-zero here + QT3DSVec4 originalClrColor = theRenderContext.GetClearColor(); + theRenderContext.SetClearColor(QT3DSVec4(0.0)); + theRenderContext.Clear(NVRenderClearValues::Color); + theRenderContext.SetClearColor(originalClrColor); + + } + void SLayerRenderData::BlendAdvancedToFB(DefaultMaterialBlendMode::Enum blendMode, + bool depthEnabled, CResourceFrameBuffer *theFB) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + AdvancedBlendModes::Enum advancedMode; + + switch (blendMode) { + case DefaultMaterialBlendMode::Overlay: + advancedMode = AdvancedBlendModes::Overlay; + break; + case DefaultMaterialBlendMode::ColorBurn: + advancedMode = AdvancedBlendModes::ColorBurn; + break; + case DefaultMaterialBlendMode::ColorDodge: + advancedMode = AdvancedBlendModes::ColorDodge; + break; + default: + Q_UNREACHABLE(); + } + // create blending FBO and texture if not existing + if (!m_AdvancedModeBlendFB) + m_AdvancedModeBlendFB = theRenderContext.CreateFrameBuffer(); + if (!m_AdvancedBlendBlendTexture) { + m_AdvancedBlendBlendTexture = theRenderContext.CreateTexture2D(); + m_AdvancedBlendBlendTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theViewport.m_Width, + theViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_AdvancedModeBlendFB->Attach(NVRenderFrameBufferAttachments::Color0, + *m_AdvancedBlendBlendTexture); + } + theRenderContext.SetRenderTarget(m_AdvancedModeBlendFB); + + // Blend transparent objects with SW fallback shaders. + // Disable depth testing as transparent objects have already been + // depth-checked; here we want to run shader for all layer pixels + if (depthEnabled) + { + theRenderContext.SetDepthTestEnabled(false); + theRenderContext.SetDepthWriteEnabled(false); + } + BlendAdvancedEquationSwFallback(m_AdvancedBlendDrawTexture, m_LayerTexture, advancedMode); + theRenderContext.SetRenderTarget(*theFB); + // setup read target + theRenderContext.SetReadTarget(m_AdvancedModeBlendFB); + theRenderContext.SetReadBuffer(NVReadFaces::Color0); + theRenderContext.BlitFramebuffer(0, 0, theViewport.m_Width, theViewport.m_Height, + 0, 0, theViewport.m_Width, theViewport.m_Height, + NVRenderClearValues::Color, + NVRenderTextureMagnifyingOp::Nearest); + } +#endif + + void SLayerRenderData::RenderToViewport() + { + if (m_LayerPrepResult->IsLayerVisible()) { + if (GetOffscreenRenderer()) { + if (m_Layer.m_Background == LayerBackground::Color) { + m_LastFrameOffscreenRenderer->RenderWithClear( + CreateOffscreenRenderEnvironment(), m_Renderer.GetContext(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), + SScene::AlwaysClear, m_Layer.m_ClearColor, &m_Layer); + } else { + m_LastFrameOffscreenRenderer->Render( + CreateOffscreenRenderEnvironment(), m_Renderer.GetContext(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), + SScene::ClearIsOptional, &m_Layer); + } + } else { + RenderDepthPass(false); + Render(); + RenderRenderWidgets(); + } + } + } + // These are meant to be pixel offsets, so you need to divide them by the width/height + // of the layer respectively. + const QT3DSVec2 s_VertexOffsets[SLayerRenderPreparationData::MAX_AA_LEVELS] = { + QT3DSVec2(-0.170840f, -0.553840f), // 1x + QT3DSVec2(0.162960f, -0.319340f), // 2x + QT3DSVec2(0.360260f, -0.245840f), // 3x + QT3DSVec2(-0.561340f, -0.149540f), // 4x + QT3DSVec2(0.249460f, 0.453460f), // 5x + QT3DSVec2(-0.336340f, 0.378260f), // 6x + QT3DSVec2(0.340000f, 0.166260f), // 7x + QT3DSVec2(0.235760f, 0.527760f), // 8x + }; + + // Blend factors are in the form of (frame blend factor, accumulator blend factor) + const QT3DSVec2 s_BlendFactors[SLayerRenderPreparationData::MAX_AA_LEVELS] = { + QT3DSVec2(0.500000f, 0.500000f), // 1x + QT3DSVec2(0.333333f, 0.666667f), // 2x + QT3DSVec2(0.250000f, 0.750000f), // 3x + QT3DSVec2(0.200000f, 0.800000f), // 4x + QT3DSVec2(0.166667f, 0.833333f), // 5x + QT3DSVec2(0.142857f, 0.857143f), // 6x + QT3DSVec2(0.125000f, 0.875000f), // 7x + QT3DSVec2(0.111111f, 0.888889f), // 8x + }; + + const QT3DSVec2 s_TemporalVertexOffsets[SLayerRenderPreparationData::MAX_TEMPORAL_AA_LEVELS] = { + QT3DSVec2(.3f, .3f), QT3DSVec2(-.3f, -.3f) + }; + + static inline void OffsetProjectionMatrix(QT3DSMat44 &inProjectionMatrix, QT3DSVec2 inVertexOffsets) + { + inProjectionMatrix.column3.x = + inProjectionMatrix.column3.x + inProjectionMatrix.column3.w * inVertexOffsets.x; + inProjectionMatrix.column3.y = + inProjectionMatrix.column3.y + inProjectionMatrix.column3.w * inVertexOffsets.y; + } + + CRegisteredString depthPassStr; + + // Render this layer's data to a texture. Required if we have any effects, + // prog AA, or if forced. + void SLayerRenderData::RenderToTexture() + { + QT3DS_ASSERT(m_LayerPrepResult->m_Flags.ShouldRenderToTexture()); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + QSize theLayerTextureDimensions = thePrepResult.GetTextureDimensions(); + QSize theLayerOriginalTextureDimensions = theLayerTextureDimensions; + NVRenderTextureFormats::Enum DepthTextureFormat = NVRenderTextureFormats::Depth24Stencil8; + NVRenderTextureFormats::Enum ColorTextureFormat = NVRenderTextureFormats::RGBA8; + if (thePrepResult.m_LastEffect + && theRenderContext.GetRenderContextType() != NVRenderContextValues::GLES2) { + if (m_Layer.m_Background != LayerBackground::Transparent) + ColorTextureFormat = NVRenderTextureFormats::R11G11B10; + else + ColorTextureFormat = NVRenderTextureFormats::RGBA16F; + } + NVRenderTextureFormats::Enum ColorSSAOTextureFormat = NVRenderTextureFormats::RGBA8; + + bool needsRender = false; + QT3DSU32 sampleCount = 1; + // check multsample mode and MSAA texture support + if (m_Layer.m_MultisampleAAMode != AAModeValues::NoAA + && theRenderContext.AreMultisampleTexturesSupported()) + sampleCount = (QT3DSU32)m_Layer.m_MultisampleAAMode; + + bool isMultisamplePass = false; + if (theRenderContext.GetRenderContextType() != NVRenderContextValues::GLES2) + isMultisamplePass = + (sampleCount > 1) || (m_Layer.m_MultisampleAAMode == AAModeValues::SSAA); + + qt3ds::render::NVRenderTextureTargetType::Enum thFboAttachTarget = + qt3ds::render::NVRenderTextureTargetType::Texture2D; + + // If the user has disabled all layer caching this has the side effect of disabling the + // progressive AA algorithm. + if (thePrepResult.m_Flags.WasLayerDataDirty() + || thePrepResult.m_Flags.WasDirty() + || m_Renderer.IsLayerCachingEnabled() == false + || thePrepResult.m_Flags.ShouldRenderToTexture()) { + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + needsRender = true; + } + + CResourceTexture2D *renderColorTexture = &m_LayerTexture; + CResourceTexture2D *renderPrepassDepthTexture = &m_LayerPrepassDepthTexture; + CResourceTexture2D *renderWidgetTexture = &m_LayerWidgetTexture; + NVRenderContextScopedProperty<bool> __multisampleEnabled( + theRenderContext, &NVRenderContext::IsMultisampleEnabled, + &NVRenderContext::SetMultisampleEnabled); + theRenderContext.SetMultisampleEnabled(false); + if (isMultisamplePass) { + renderColorTexture = &m_LayerMultisampleTexture; + renderPrepassDepthTexture = &m_LayerMultisamplePrepassDepthTexture; + renderWidgetTexture = &m_LayerMultisampleWidgetTexture; + // for SSAA we don't use MS textures + if (m_Layer.m_MultisampleAAMode != AAModeValues::SSAA) + thFboAttachTarget = qt3ds::render::NVRenderTextureTargetType::Texture2D_MS; + } + QT3DSU32 maxTemporalPassIndex = m_Layer.m_TemporalAAEnabled ? 2 : 0; + + // If all the dimensions match then we do not have to re-render the layer. + if (m_LayerTexture.TextureMatches(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), ColorTextureFormat) + && (!thePrepResult.m_Flags.RequiresDepthTexture() + || m_LayerDepthTexture.TextureMatches(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + DepthTextureFormat)) + && m_ProgressiveAAPassIndex >= thePrepResult.m_MaxAAPassIndex + && m_NonDirtyTemporalAAPassIndex >= maxTemporalPassIndex && needsRender == false) { + return; + } + + // adjust render size for SSAA + if (m_Layer.m_MultisampleAAMode == AAModeValues::SSAA) { + QT3DSU32 ow, oh; + CRendererUtil::GetSSAARenderSize(theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), + ow, oh); + theLayerTextureDimensions = QSize(ow, oh); + } + + // If our pass index == thePreResult.m_MaxAAPassIndex then + // we shouldn't get into here. + + IResourceManager &theResourceManager = m_Renderer.GetQt3DSContext().GetResourceManager(); + bool hadLayerTexture = true; + + if (renderColorTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + ColorTextureFormat, sampleCount)) { + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + hadLayerTexture = false; + } + + if (thePrepResult.m_Flags.RequiresDepthTexture()) { + // The depth texture doesn't need to be multisample, the prepass depth does. + if (m_LayerDepthTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + DepthTextureFormat)) { + // Depth textures are generally not bilinear filtered. + m_LayerDepthTexture->SetMinFilter(NVRenderTextureMinifyingOp::Nearest); + m_LayerDepthTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Nearest); + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + } + } + + if (thePrepResult.m_Flags.RequiresSsaoPass()) { + if (m_LayerSsaoTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + ColorSSAOTextureFormat)) { + m_LayerSsaoTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + m_LayerSsaoTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + } + } + + QT3DS_ASSERT(!thePrepResult.m_Flags.RequiresDepthTexture() || m_LayerDepthTexture); + QT3DS_ASSERT(!thePrepResult.m_Flags.RequiresSsaoPass() || m_LayerSsaoTexture); + + CResourceTexture2D theLastLayerTexture(theResourceManager); + SLayerProgAABlendShader *theBlendShader = NULL; + QT3DSU32 aaFactorIndex = 0; + bool isProgressiveAABlendPass = + m_ProgressiveAAPassIndex && m_ProgressiveAAPassIndex < thePrepResult.m_MaxAAPassIndex; + bool isTemporalAABlendPass = m_Layer.m_TemporalAAEnabled && m_ProgressiveAAPassIndex == 0; + + if (isProgressiveAABlendPass || isTemporalAABlendPass) { + theBlendShader = m_Renderer.GetLayerProgAABlendShader(); + if (theBlendShader) { + m_LayerTexture.EnsureTexture(theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), + ColorTextureFormat); + QT3DSVec2 theVertexOffsets; + if (isProgressiveAABlendPass) { + theLastLayerTexture.StealTexture(m_LayerTexture); + aaFactorIndex = (m_ProgressiveAAPassIndex - 1); + theVertexOffsets = s_VertexOffsets[aaFactorIndex]; + } else { + if (m_TemporalAATexture.GetTexture()) + theLastLayerTexture.StealTexture(m_TemporalAATexture); + else { + if (hadLayerTexture) { + theLastLayerTexture.StealTexture(m_LayerTexture); + } + } + theVertexOffsets = s_TemporalVertexOffsets[m_TemporalAAPassIndex]; + ++m_TemporalAAPassIndex; + ++m_NonDirtyTemporalAAPassIndex; + m_TemporalAAPassIndex = m_TemporalAAPassIndex % MAX_TEMPORAL_AA_LEVELS; + } + if (theLastLayerTexture.GetTexture()) { + theVertexOffsets.x = + theVertexOffsets.x / (theLayerOriginalTextureDimensions.width() / 2.0f); + theVertexOffsets.y = + theVertexOffsets.y / (theLayerOriginalTextureDimensions.height() / 2.0f); + // Run through all models and update MVP. + // run through all texts and update MVP. + // run through all path and update MVP. + + // TODO - optimize this exact matrix operation. + for (QT3DSU32 idx = 0, end = m_ModelContexts.size(); idx < end; ++idx) { + QT3DSMat44 &originalProjection(m_ModelContexts[idx]->m_ModelViewProjection); + OffsetProjectionMatrix(originalProjection, theVertexOffsets); + } + for (QT3DSU32 idx = 0, end = m_OpaqueObjects.size(); idx < end; ++idx) { + if (m_OpaqueObjects[idx]->m_RenderableFlags.IsPath()) { + SPathRenderable &theRenderable = + static_cast<SPathRenderable &>(*m_OpaqueObjects[idx]); + OffsetProjectionMatrix(theRenderable.m_ModelViewProjection, + theVertexOffsets); + } + } + for (QT3DSU32 idx = 0, end = m_TransparentObjects.size(); idx < end; ++idx) { + if (m_TransparentObjects[idx]->m_RenderableFlags.IsText()) { + STextRenderable &theRenderable = + 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]); + OffsetProjectionMatrix(theRenderable.m_ModelViewProjection, + theVertexOffsets); + } + } + } + } + } + if (theLastLayerTexture.GetTexture() == NULL) { + isProgressiveAABlendPass = false; + isTemporalAABlendPass = false; + } + // Sometimes we will have stolen the render texture. + renderColorTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), ColorTextureFormat, + sampleCount); + + if (!isTemporalAABlendPass) + m_TemporalAATexture.ReleaseTexture(); + + // Allocating a frame buffer can cause it to be bound, so we need to save state before this + // happens. + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuf( + theRenderContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + // Match the bit depth of the current render target to avoid popping when we switch from aa + // to non aa layers + // We have to all this here in because once we change the FB by allocating an FB we are + // screwed. + NVRenderTextureFormats::Enum theDepthFormat(GetDepthBufferFormat()); + NVRenderFrameBufferAttachments::Enum theDepthAttachmentFormat( + GetFramebufferDepthAttachmentFormat(theDepthFormat)); + + // Definitely disable the scissor rect if it is running right now. + NVRenderContextScopedProperty<bool> __scissorEnabled( + theRenderContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + CResourceFrameBuffer theFB(theResourceManager); + // Allocates the frame buffer which has the side effect of setting the current render target + // to that frame buffer. + theFB.EnsureFrameBuffer(); + + bool hasDepthObjects = m_OpaqueObjects.size() > 0; + bool requiresDepthStencilBuffer = + hasDepthObjects || thePrepResult.m_Flags.RequiresStencilBuffer(); + NVRenderRect theNewViewport(0, 0, theLayerTextureDimensions.width(), + theLayerTextureDimensions.height()); + { + theRenderContext.SetRenderTarget(theFB); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theRenderContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + theNewViewport); + QT3DSVec4 clearColor(0.0f); + if (m_Layer.m_Background == LayerBackground::Color) + clearColor = m_Layer.m_ClearColor; + + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theRenderContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + clearColor); + if (requiresDepthStencilBuffer) { + if (renderPrepassDepthTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + theDepthFormat, sampleCount)) { + (*renderPrepassDepthTexture)->SetMinFilter(NVRenderTextureMinifyingOp::Nearest); + (*renderPrepassDepthTexture) + ->SetMagFilter(NVRenderTextureMagnifyingOp::Nearest); + } + } + + if (thePrepResult.m_Flags.RequiresDepthTexture() && m_ProgressiveAAPassIndex == 0) { + // Setup FBO with single depth buffer target. + // Note this does not use multisample. + NVRenderFrameBufferAttachments::Enum theAttachment = + GetFramebufferDepthAttachmentFormat(DepthTextureFormat); + theFB->Attach(theAttachment, *m_LayerDepthTexture); + + // In this case transparent objects also may write their depth. + RenderDepthPass(true); + theFB->Attach(theAttachment, NVRenderTextureOrRenderBuffer()); + } + + if (thePrepResult.m_Flags.RequiresSsaoPass() && m_ProgressiveAAPassIndex == 0 + && m_Camera != nullptr) { + StartProfiling("AO pass", false); + // Setup FBO with single color buffer target + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerSsaoTexture); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + RenderAoPass(); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer()); + EndProfiling("AO pass"); + } + + if (thePrepResult.m_Flags.RequiresShadowMapPass() && m_ProgressiveAAPassIndex == 0) { + // shadow map path + RenderShadowMapPass(&theFB); + } + + if (sampleCount > 1) { + theRenderContext.SetMultisampleEnabled(true); + } + + qt3ds::render::NVRenderClearFlags clearFlags = qt3ds::render::NVRenderClearValues::Color; + + // render depth prepass + if ((*renderPrepassDepthTexture)) { + theFB->Attach(theDepthAttachmentFormat, **renderPrepassDepthTexture, + thFboAttachTarget); + + if (m_Layer.m_Flags.IsLayerEnableDepthPrepass()) { + StartProfiling("Depth pass", false); + RenderDepthPass(false); + EndProfiling("Depth pass"); + } else { + clearFlags |= (qt3ds::render::NVRenderClearValues::Depth); + clearFlags |= (qt3ds::render::NVRenderClearValues::Stencil); + // enable depth write for the clear below + theRenderContext.SetDepthWriteEnabled(true); + } + } + + theFB->Attach(NVRenderFrameBufferAttachments::Color0, **renderColorTexture, + thFboAttachTarget); + if (m_Layer.m_Background != LayerBackground::Unspecified) + theRenderContext.Clear(clearFlags); + + // We don't clear the depth buffer because the layer render code we are about to call + // will do this. + StartProfiling("Render pass", false); + Render(&theFB); + // Debug measure to view the depth map to ensure we're rendering it correctly. + //if (m_Layer.m_TemporalAAEnabled) { + // RenderFakeDepthMapPass(m_ShadowMapManager->GetShadowMapEntry(0)->m_DepthMap, + // m_ShadowMapManager->GetShadowMapEntry(0)->m_DepthCube); + //} + EndProfiling("Render pass"); + + // Now before going further, we downsample and resolve the multisample information. + // This allows all algorithms running after + // this point to run unchanged. + if (isMultisamplePass) { + if (m_Layer.m_MultisampleAAMode != AAModeValues::SSAA) { + // Resolve the FBO to the layer texture + CRendererUtil::ResolveMutisampleFBOColorOnly( + theResourceManager, m_LayerTexture, theRenderContext, + theLayerTextureDimensions.width(), theLayerTextureDimensions.height(), + ColorTextureFormat, *theFB); + + theRenderContext.SetMultisampleEnabled(false); + } else { + // Resolve the FBO to the layer texture + CRendererUtil::ResolveSSAAFBOColorOnly( + theResourceManager, m_LayerTexture, + theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), theRenderContext, + theLayerTextureDimensions.width(), theLayerTextureDimensions.height(), + ColorTextureFormat, *theFB); + } + } + + // CN - when I tried to get anti-aliased widgets I lost all transparency on the widget + // layer which made it overwrite the object you were + // manipulating. When I tried to use parallel nsight on it the entire studio + // application crashed on startup. + if (NeedsWidgetTexture()) { + m_LayerWidgetTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + NVRenderTextureFormats::RGBA8); + theRenderContext.SetRenderTarget(theFB); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerWidgetTexture); + theFB->Attach(GetFramebufferDepthAttachmentFormat(DepthTextureFormat), + *m_LayerDepthTexture); + theRenderContext.SetClearColor(QT3DSVec4(0.0f)); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + // We should already have the viewport and everything setup for this. + RenderRenderWidgets(); + } + + if (theLastLayerTexture.GetTexture() != NULL + && (isProgressiveAABlendPass || isTemporalAABlendPass)) { + theRenderContext.SetViewport( + NVRenderRect(0, 0, theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height())); + CResourceTexture2D targetTexture( + theResourceManager, theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), ColorTextureFormat); + theFB->Attach(theDepthAttachmentFormat, + NVRenderTextureOrRenderBuffer()); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *targetTexture); + QT3DSVec2 theBlendFactors; + if (isProgressiveAABlendPass) + theBlendFactors = s_BlendFactors[aaFactorIndex]; + else + theBlendFactors = QT3DSVec2(.5f, .5f); + + theRenderContext.SetDepthTestEnabled(false); + theRenderContext.SetBlendingEnabled(false); + theRenderContext.SetCullingEnabled(false); + theRenderContext.SetActiveShader(theBlendShader->m_Shader); + theBlendShader->m_AccumSampler.Set(theLastLayerTexture); + theBlendShader->m_LastFrame.Set(m_LayerTexture); + theBlendShader->m_BlendFactors.Set(theBlendFactors); + m_Renderer.RenderQuad(); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + if (isTemporalAABlendPass) + m_TemporalAATexture.StealTexture(m_LayerTexture); + m_LayerTexture.StealTexture(targetTexture); + } + + m_LayerTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + m_LayerTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + + // Don't remember why needs widget texture is false here. + // Should have commented why progAA plus widgets is a fail. + if (m_ProgressiveAAPassIndex < thePrepResult.m_MaxAAPassIndex + && NeedsWidgetTexture() == false) + ++m_ProgressiveAAPassIndex; + +// now we render all post effects +#ifdef QT3DS_CACHED_POST_EFFECT + ApplyLayerPostEffects(); +#endif + + if (m_LayerPrepassDepthTexture) { + // Detach any depth buffers. + theFB->Attach(theDepthAttachmentFormat, qt3ds::render::NVRenderTextureOrRenderBuffer(), + thFboAttachTarget); + } + + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer(), thFboAttachTarget); + // Let natural scoping rules destroy the other stuff. + } + } + + void SLayerRenderData::ApplyLayerPostEffects() + { + if (m_Layer.m_FirstEffect == NULL) { + if (m_LayerCachedTexture) { + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + return; + } + + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // we use the non MSAA buffer for the effect + NVRenderTexture2D *theLayerColorTexture = m_LayerTexture; + NVRenderTexture2D *theLayerDepthTexture = m_LayerDepthTexture; + + NVRenderTexture2D *theCurrentTexture = theLayerColorTexture; + for (SEffect *theEffect = m_Layer.m_FirstEffect; theEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsActive() && m_Camera) { + StartProfiling(theEffect->m_ClassName, false); + + NVRenderTexture2D *theRenderedEffect = theEffectSystem.RenderEffect( + SEffectRenderArgument(*theEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture)); + + EndProfiling(theEffect->m_ClassName); + + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + + theCurrentTexture = theRenderedEffect; + + if (!theRenderedEffect) { + QString errorMsg = QObject::tr("Failed to compile \"%1\" effect.\nConsider" + " removing it from the presentation.") + .arg(theEffect->m_ClassName.c_str()); + QT3DS_ALWAYS_ASSERT_MESSAGE(errorMsg.toUtf8()); + break; + } + } + } + + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) { + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + + if (theCurrentTexture != m_LayerTexture) + m_LayerCachedTexture = theCurrentTexture; + } + + inline bool AnyCompletelyNonTransparentObjects(TRenderableObjectList &inObjects) + { + for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) { + if (inObjects[idx]->m_RenderableFlags.IsCompletelyTransparent() == false) + return true; + } + return false; + } + + void SLayerRenderData::RunnableRenderToViewport(qt3ds::render::NVRenderFrameBuffer *theFB) + { + // If we have an effect, an opaque object, or any transparent objects that aren't completely + // transparent + // or an offscreen renderer or a layer widget texture + // Then we can't possible affect the resulting render target. + bool needsToRender = m_Layer.m_FirstEffect != NULL || m_OpaqueObjects.empty() == false + || AnyCompletelyNonTransparentObjects(m_TransparentObjects) || GetOffscreenRenderer() + || m_LayerWidgetTexture || m_BoundingRectColor.hasValue() + || m_Layer.m_Background == LayerBackground::Color; + + if (needsToRender == false) + return; + + NVRenderContext &theContext(m_Renderer.GetContext()); + theContext.resetStates(); + + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + qt3ds::render::NVRenderRect theCurrentViewport = theContext.GetViewport(); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + NVRenderContextScopedProperty<bool> theScissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + NVRenderContextScopedProperty<qt3ds::render::NVRenderRect> theScissorRect( + theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderRectF theScreenRect(thePrepResult.GetLayerToPresentationViewport()); + + bool blendingEnabled = m_Layer.m_Background != LayerBackground::Unspecified; + if (!thePrepResult.m_Flags.ShouldRenderToTexture()) { + theContext.SetViewport( + m_LayerPrepResult->GetLayerToPresentationViewport().ToIntegerRect()); + theContext.SetScissorTestEnabled(true); + theContext.SetScissorRect( + m_LayerPrepResult->GetLayerToPresentationScissorRect().ToIntegerRect()); + if (m_Layer.m_Background == LayerBackground::Color) { + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + m_Layer.m_ClearColor); + theContext.Clear(NVRenderClearValues::Color); + } + RenderToViewport(); + } else { +// First, render the layer along with whatever progressive AA is appropriate. +// The render graph should have taken care of the render to texture step. +#ifdef QT3DS_CACHED_POST_EFFECT + NVRenderTexture2D *theLayerColorTexture = + (m_LayerCachedTexture) ? m_LayerCachedTexture : m_LayerTexture; +#else + // Then render all but the last effect + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // we use the non MSAA buffer for the effect + NVRenderTexture2D *theLayerColorTexture = m_LayerTexture; + NVRenderTexture2D *theLayerDepthTexture = m_LayerDepthTexture; + + NVRenderTexture2D *theCurrentTexture = theLayerColorTexture; + for (SEffect *theEffect = m_Layer.m_FirstEffect; + theEffect && theEffect != thePrepResult.m_LastEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsActive() && m_Camera) { + StartProfiling(theEffect->m_ClassName, false); + + NVRenderTexture2D *theRenderedEffect = theEffectSystem.RenderEffect( + SEffectRenderArgument(*theEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture)); + + EndProfiling(theEffect->m_ClassName); + + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + + theCurrentTexture = theRenderedEffect; + } + } +#endif + // Now the last effect or straight to the scene if we have no last effect + // There are two cases we need to consider here. The first is when we shouldn't + // transform + // the result and thus we need to setup an MVP that just maps to the viewport width and + // height. + // The second is when we are expected to render to the scene using some global + // transform. + QT3DSMat44 theFinalMVP(QT3DSMat44::createIdentity()); + SCamera theTempCamera; + NVRenderRect theLayerViewport( + thePrepResult.GetLayerToPresentationViewport().ToIntegerRect()); + NVRenderRect theLayerClip( + thePrepResult.GetLayerToPresentationScissorRect().ToIntegerRect()); + + { + QT3DSMat33 ignored; + QT3DSMat44 theViewProjection; + // We could cache these variables + theTempCamera.m_Flags.SetOrthographic(true); + theTempCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + // Move the camera back far enough that we can see everything + QT3DSF32 theCameraSetback(10); + // Attempt to ensure the layer can never be clipped. + theTempCamera.m_Position.z = -theCameraSetback; + theTempCamera.m_ClipFar = 2.0f * theCameraSetback; + // Render the layer texture to the entire viewport. + SCameraGlobalCalculationResult theResult = theTempCamera.CalculateGlobalVariables( + theLayerViewport, + QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, (QT3DSF32)theLayerViewport.m_Height)); + theTempCamera.CalculateViewProjectionMatrix(theViewProjection); + SNode theTempNode; + theFinalMVP = theViewProjection; + qt3ds::render::NVRenderBlendFunctionArgument blendFunc; + qt3ds::render::NVRenderBlendEquationArgument blendEqu; + + switch (m_Layer.m_BlendType) { + case LayerBlendTypes::Screen: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Multiply: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::DstColor, NVRenderDstBlendFunc::Zero, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Add: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Subtract: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ReverseSubtract, + NVRenderBlendEquation::ReverseSubtract); + break; + case LayerBlendTypes::Overlay: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Overlay, NVRenderBlendEquation::Overlay); + break; + case LayerBlendTypes::ColorBurn: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorBurn, NVRenderBlendEquation::ColorBurn); + break; + case LayerBlendTypes::ColorDodge: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorDodge, NVRenderBlendEquation::ColorDodge); + break; + default: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + } + theContext.SetBlendFunction(blendFunc); + theContext.SetBlendEquation(blendEqu); + theContext.SetBlendingEnabled(blendingEnabled); + theContext.SetDepthTestEnabled(false); + } + + { + theContext.SetScissorTestEnabled(true); + theContext.SetViewport(theLayerViewport); + theContext.SetScissorRect(theLayerClip); + + // Remember the camera we used so we can get a valid pick ray + m_SceneCamera = theTempCamera; + theContext.SetDepthTestEnabled(false); +#ifndef QT3DS_CACHED_POST_EFFECT + if (thePrepResult.m_LastEffect && m_Camera) { + StartProfiling(thePrepResult.m_LastEffect->m_ClassName, false); + // inUseLayerMPV is true then we are rendering directly to the scene and thus we + // should enable blending + // for the final render pass. Else we should leave it. + theEffectSystem.RenderEffect( + SEffectRenderArgument(*thePrepResult.m_LastEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture), + theFinalMVP, blendingEnabled); + EndProfiling(thePrepResult.m_LastEffect->m_ClassName); + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + } else +#endif + { + theContext.SetCullingEnabled(false); + theContext.SetBlendingEnabled(blendingEnabled); + theContext.SetDepthTestEnabled(false); +#ifdef ADVANCED_BLEND_SW_FALLBACK + NVScopedRefCounted<NVRenderTexture2D> screenTexture = + m_Renderer.GetLayerBlendTexture(); + NVScopedRefCounted<NVRenderFrameBuffer> blendFB = m_Renderer.GetBlendFB(); + + // Layer blending for advanced blending modes if SW fallback is needed + // rendering to FBO and blending with separate shader + if (screenTexture) { + // Blending is enabled only if layer background has been chosen transparent + // Layers with advanced blending modes + if (blendingEnabled && (m_Layer.m_BlendType == LayerBlendTypes::Overlay || + m_Layer.m_BlendType == LayerBlendTypes::ColorBurn || + m_Layer.m_BlendType == LayerBlendTypes::ColorDodge)) { + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(false); + + // Get part matching to layer from screen texture and + // use that for blending + qt3ds::render::NVRenderTexture2D *blendBlitTexture; + blendBlitTexture = theContext.CreateTexture2D(); + blendBlitTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderTextureFormats::RGBA8); + qt3ds::render::NVRenderFrameBuffer *blitFB; + blitFB = theContext.CreateFrameBuffer(); + blitFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*blendBlitTexture)); + blendFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*screenTexture)); + theContext.SetRenderTarget(blitFB); + theContext.SetReadTarget(blendFB); + theContext.SetReadBuffer(NVReadFaces::Color0); + theContext.BlitFramebuffer(theLayerViewport.m_X, theLayerViewport.m_Y, + theLayerViewport.m_Width + + theLayerViewport.m_X, + theLayerViewport.m_Height + + theLayerViewport.m_Y, + 0, 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderClearValues::Color, + NVRenderTextureMagnifyingOp::Nearest); + + qt3ds::render::NVRenderTexture2D *blendResultTexture; + blendResultTexture = theContext.CreateTexture2D(); + blendResultTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderTextureFormats::RGBA8); + qt3ds::render::NVRenderFrameBuffer *resultFB; + resultFB = theContext.CreateFrameBuffer(); + resultFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*blendResultTexture)); + theContext.SetRenderTarget(resultFB); + + AdvancedBlendModes::Enum advancedMode; + switch (m_Layer.m_BlendType) { + case LayerBlendTypes::Overlay: + advancedMode = AdvancedBlendModes::Overlay; + break; + case LayerBlendTypes::ColorBurn: + advancedMode = AdvancedBlendModes::ColorBurn; + break; + case LayerBlendTypes::ColorDodge: + advancedMode = AdvancedBlendModes::ColorDodge; + break; + default: + advancedMode = AdvancedBlendModes::None; + break; + } + + theContext.SetViewport(NVRenderRect(0, 0, theLayerViewport.m_Width, + theLayerViewport.m_Height)); + BlendAdvancedEquationSwFallback(theLayerColorTexture, blendBlitTexture, + advancedMode); + blitFB->release(); + // save blending result to screen texture for use with other layers + theContext.SetViewport(theLayerViewport); + theContext.SetRenderTarget(blendFB); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *blendResultTexture); + // render the blended result + theContext.SetRenderTarget(theFB); + theContext.SetScissorTestEnabled(true); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *blendResultTexture); + resultFB->release(); + } else { + // Layers with normal blending modes + // save result for future use + theContext.SetViewport(theLayerViewport); + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(true); + theContext.SetRenderTarget(blendFB); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + theContext.SetRenderTarget(theFB); + theContext.SetScissorTestEnabled(true); + theContext.SetViewport(theCurrentViewport); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + } + } else { + // No advanced blending SW fallback needed + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + } +#else + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); +#endif + } + if (m_LayerWidgetTexture) { + theContext.SetBlendingEnabled(false); + m_Renderer.SetupWidgetLayer(); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderRectF thePresRect(thePrepResult.GetPresentationViewport()); + NVRenderRectF theLayerRect(thePrepResult.GetLayerToPresentationViewport()); + + // Ensure we remove any offsetting in the layer rect that was caused simply by + // the + // presentation rect offsetting but then use a new rect. + NVRenderRectF theWidgetLayerRect(theLayerRect.m_X - thePresRect.m_X, + theLayerRect.m_Y - thePresRect.m_Y, + theLayerRect.m_Width, theLayerRect.m_Height); + theContext.SetScissorTestEnabled(false); + theContext.SetViewport(theWidgetLayerRect.ToIntegerRect()); + m_Renderer.RenderQuad( + QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *m_LayerWidgetTexture); + } + } + } // End offscreen render code. + + if (m_BoundingRectColor.hasValue()) { + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + NVRenderContextScopedProperty<bool> theScissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + NVRenderContextScopedProperty<qt3ds::render::NVRenderRect> theScissorRect( + theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect); + m_Renderer.SetupWidgetLayer(); + // Setup a simple viewport to render to the entire presentation viewport. + theContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)thePrepResult.GetPresentationViewport().m_Width, + (QT3DSU32)thePrepResult.GetPresentationViewport().m_Height)); + + NVRenderRectF thePresRect(thePrepResult.GetPresentationViewport()); + + // Remove any offsetting from the presentation rect since the widget layer is a + // stand-alone fbo. + NVRenderRectF theWidgetScreenRect(theScreenRect.m_X - thePresRect.m_X, + theScreenRect.m_Y - thePresRect.m_Y, + theScreenRect.m_Width, theScreenRect.m_Height); + theContext.SetScissorTestEnabled(false); + m_Renderer.DrawScreenRect(theWidgetScreenRect, *m_BoundingRectColor); + } + theContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha)); + theContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + } + +#define RENDER_FRAME_NEW(type) QT3DS_NEW(m_Renderer.GetPerFrameAllocator(), type) + + void SLayerRenderData::AddLayerRenderStep() + { + SStackPerfTimer __perfTimer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::AddLayerRenderStep"); + QT3DS_ASSERT(m_Camera); + if (!m_Camera) + return; + + IRenderList &theGraph(m_Renderer.GetQt3DSContext().GetRenderList()); + + qt3ds::render::NVRenderRect theCurrentViewport = theGraph.GetViewport(); + if (!m_LayerPrepResult.hasValue()) + PrepareForRender( + QSize(theCurrentViewport.m_Width, theCurrentViewport.m_Height)); + } + + void SLayerRenderData::PrepareForRender() + { + // When we render to the scene itself (as opposed to an offscreen buffer somewhere) + // then we use the MVP of the layer somewhat. + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + PrepareForRender( + QSize((QT3DSU32)theViewport.m_Width, (QT3DSU32)theViewport.m_Height)); + } + + void SLayerRenderData::ResetForFrame() + { + SLayerRenderPreparationData::ResetForFrame(); + m_BoundingRectColor.setEmpty(); + } + + void SLayerRenderData::PrepareAndRender(const QT3DSMat44 &inViewProjection) + { + TRenderableObjectList theTransparentObjects(m_TransparentObjects); + TRenderableObjectList theOpaqueObjects(m_OpaqueObjects); + theTransparentObjects.clear(); + theOpaqueObjects.clear(); + m_ModelContexts.clear(); + SLayerRenderPreparationResultFlags theFlags; + PrepareRenderablesForRender(inViewProjection, Empty(), 1.0, theFlags); + RenderDepthPass(false); + Render(); + } + + struct SLayerRenderToTextureRunnable : public IRenderTask + { + SLayerRenderData &m_Data; + SLayerRenderToTextureRunnable(SLayerRenderData &d) + : m_Data(d) + { + } + + void Run() override { m_Data.RenderToTexture(); } + }; + + static inline OffscreenRendererDepthValues::Enum + GetOffscreenRendererDepthValue(NVRenderTextureFormats::Enum inBufferFormat) + { + switch (inBufferFormat) { + case NVRenderTextureFormats::Depth32: + return OffscreenRendererDepthValues::Depth32; + case NVRenderTextureFormats::Depth24: + return OffscreenRendererDepthValues::Depth24; + case NVRenderTextureFormats::Depth24Stencil8: + return OffscreenRendererDepthValues::Depth24; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case NVRenderTextureFormats::Depth16: + return OffscreenRendererDepthValues::Depth16; + } + } + + SOffscreenRendererEnvironment SLayerRenderData::CreateOffscreenRenderEnvironment() + { + OffscreenRendererDepthValues::Enum theOffscreenDepth( + GetOffscreenRendererDepthValue(GetDepthBufferFormat())); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + return SOffscreenRendererEnvironment(theViewport.m_Width, theViewport.m_Height, + NVRenderTextureFormats::RGBA8, theOffscreenDepth, + false, AAModeValues::NoAA); + } + + IRenderTask &SLayerRenderData::CreateRenderToTextureRunnable() + { + return *RENDER_FRAME_NEW(SLayerRenderToTextureRunnable)(*this); + } + + void SLayerRenderData::addRef() { atomicIncrement(&mRefCount); } + + void SLayerRenderData::release() { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Allocator); } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h new file mode 100644 index 0000000..4e237b0 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_LAYER_RENDER_DATA_H +#define QT3DS_RENDERER_IMPL_LAYER_RENDER_DATA_H +#include "Qt3DSRender.h" +#include "Qt3DSRendererImplLayerRenderPreparationData.h" +#include "Qt3DSRenderResourceBufferObjects.h" + +namespace qt3ds { +namespace render { + +struct AdvancedBlendModes +{ + enum Enum { + None = 0, + Overlay, + ColorBurn, + ColorDodge + }; +}; + struct QT3DS_AUTOTEST_EXPORT SLayerRenderData : public SLayerRenderPreparationData + { + + // Layers can be rendered offscreen for many reasons; effects, progressive aa, + // or just because a flag forces it. If they are rendered offscreen we can then + // cache the result so we don't render the layer again if it isn't dirty. + CResourceTexture2D m_LayerTexture; + CResourceTexture2D m_TemporalAATexture; + // Sometimes we need to render our depth buffer to a depth texture. + CResourceTexture2D m_LayerDepthTexture; + CResourceTexture2D m_LayerPrepassDepthTexture; + CResourceTexture2D m_LayerWidgetTexture; + CResourceTexture2D m_LayerSsaoTexture; + // if we render multisampled we need resolve buffers + CResourceTexture2D m_LayerMultisampleTexture; + CResourceTexture2D m_LayerMultisamplePrepassDepthTexture; + CResourceTexture2D m_LayerMultisampleWidgetTexture; + // the texture contains the render result inclusive post effects + NVRenderTexture2D *m_LayerCachedTexture; + + NVRenderTexture2D *m_AdvancedBlendDrawTexture; + NVRenderTexture2D *m_AdvancedBlendBlendTexture; + qt3ds::render::NVRenderFrameBuffer *m_AdvancedModeDrawFB; + qt3ds::render::NVRenderFrameBuffer *m_AdvancedModeBlendFB; + + // True if this layer was rendered offscreen. + // If this object has no value then this layer wasn't rendered at all. + SOffscreenRendererEnvironment m_LastOffscreenRenderEnvironment; + + // GPU profiler per layer + NVScopedRefCounted<IRenderProfiler> m_LayerProfilerGpu; + + SCamera m_SceneCamera; + QT3DSVec2 m_SceneDimensions; + + // ProgressiveAA algorithm details. + QT3DSU32 m_ProgressiveAAPassIndex; + // Increments every frame regardless to provide appropriate jittering + QT3DSU32 m_TemporalAAPassIndex; + // Ensures we don't stop on an in-between frame; we will run two frames after the dirty flag + // is clear. + QT3DSU32 m_NonDirtyTemporalAAPassIndex; + QT3DSF32 m_TextScale; + + volatile QT3DSI32 mRefCount; + Option<QT3DSVec3> m_BoundingRectColor; + NVRenderTextureFormats::Enum m_DepthBufferFormat; + + QSize m_previousDimensions; + + SLayerRenderData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer); + + virtual ~SLayerRenderData(); + + void PrepareForRender(); + + // Internal Call + void PrepareForRender(const QSize &inViewportDimensions) override; + + NVRenderTextureFormats::Enum GetDepthBufferFormat(); + NVRenderFrameBufferAttachments::Enum + GetFramebufferDepthAttachmentFormat(NVRenderTextureFormats::Enum depthFormat); + + // Render this layer assuming viewport and RT are setup. Just renders exactly this item + // no effects. + void RenderDepthPass(bool inEnableTransparentDepthWrite = false); + void RenderAoPass(); + void RenderFakeDepthMapPass(NVRenderTexture2D *theDepthTex, + NVRenderTextureCube *theDepthCube); + void RenderShadowMapPass(CResourceFrameBuffer *theFB); + void RenderShadowCubeBlurPass(CResourceFrameBuffer *theFB, NVRenderTextureCube *target0, + NVRenderTextureCube *target1, QT3DSF32 filterSz, QT3DSF32 clipFar); + void RenderShadowMapBlurPass(CResourceFrameBuffer *theFB, NVRenderTexture2D *target0, + NVRenderTexture2D *target1, QT3DSF32 filterSz, QT3DSF32 clipFar); + + void Render(CResourceFrameBuffer *theFB = NULL); + void ResetForFrame() override; + + void CreateGpuProfiler(); + void StartProfiling(CRegisteredString &nameID, bool sync); + void EndProfiling(CRegisteredString &nameID); + void StartProfiling(const char *nameID, bool sync); + void EndProfiling(const char *nameID); + void AddVertexCount(QT3DSU32 count); + + void RenderToViewport(); + // Render this layer's data to a texture. Required if we have any effects, + // prog AA, or if forced. + void RenderToTexture(); + + void ApplyLayerPostEffects(); + + void RunnableRenderToViewport(qt3ds::render::NVRenderFrameBuffer *theFB); + + void AddLayerRenderStep(); + + void RenderRenderWidgets(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + void BlendAdvancedEquationSwFallback(NVRenderTexture2D *drawTexture, + NVRenderTexture2D *m_LayerTexture, + AdvancedBlendModes::Enum blendMode); +#endif + // test method to render this layer to a given view projection without running the entire + // 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); + + SOffscreenRendererEnvironment CreateOffscreenRenderEnvironment() override; + IRenderTask &CreateRenderToTextureRunnable() override; + + void addRef(); + void release(); + + protected: + // Used for both the normal passes and the depth pass. + // When doing the depth pass, we disable blending completely because it does not really make + // sense + // to write blend equations into + void RunRenderPass(TRenderRenderableFunction renderFn, bool inEnableBlending, + bool inEnableDepthWrite, bool inEnableTransparentDepthWrite, + QT3DSU32 indexLight, const SCamera &inCamera, + CResourceFrameBuffer *theFB = NULL); +#ifdef ADVANCED_BLEND_SW_FALLBACK + //Functions for advanced blending mode fallback + void SetupDrawFB(bool depthEnabled); + void BlendAdvancedToFB(DefaultMaterialBlendMode::Enum blendMode, bool depthEnabled, + CResourceFrameBuffer *theFB); +#endif + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp new file mode 100644 index 0000000..07ee513 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSTextRenderer.h" + +using namespace qt3ds::render; + +namespace { +// left/top +QT3DSF32 GetMinValue(QT3DSF32 start, QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return start + value; + + return start + (value * width / 100.0f); +} + +// width/height +QT3DSF32 GetValueLen(QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return value; + + return width * value / 100.0f; +} + +// right/bottom +QT3DSF32 GetMaxValue(QT3DSF32 start, QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return start + width - value; + + return start + width - (value * width / 100.0f); +} + +QT3DSVec2 ToRectRelativeCoords(const QT3DSVec2 &inCoords, const NVRenderRectF &inRect) +{ + return QT3DSVec2(inCoords.x - inRect.m_X, inCoords.y - inRect.m_Y); +} +} + +SLayerRenderHelper::SLayerRenderHelper() + : m_Layer(NULL) + , m_Camera(NULL) + , m_Offscreen(false) +{ +} + +SLayerRenderHelper::SLayerRenderHelper(const NVRenderRectF &inPresentationViewport, + const NVRenderRectF &inPresentationScissor, + const QT3DSVec2 &inPresentationDesignDimensions, + SLayer &inLayer, bool inOffscreen, + qt3ds::render::ScaleModes::Enum inScaleMode, + qt3ds::QT3DSVec2 inScaleFactor) + : m_PresentationViewport(inPresentationViewport) + , m_PresentationScissor(inPresentationScissor) + , m_PresentationDesignDimensions(inPresentationDesignDimensions) + , m_Layer(&inLayer) + , m_Offscreen(inOffscreen) + , m_ScaleMode(inScaleMode) + , m_ScaleFactor(inScaleFactor) +{ + { + QT3DSF32 left = m_Layer->m_Left; + QT3DSF32 right = m_Layer->m_Right; + QT3DSF32 width = m_Layer->m_Width; + + if (m_ScaleMode == qt3ds::render::ScaleModes::FitSelected) { + if (m_Layer->m_LeftUnits == LayerUnitTypes::Pixels) + left *= m_ScaleFactor.x; + + if (m_Layer->m_RightUnits == LayerUnitTypes::Pixels) + right *= m_ScaleFactor.x; + + if (m_Layer->m_WidthUnits == LayerUnitTypes::Pixels) + width *= m_ScaleFactor.x; + } + + QT3DSF32 horzMin = GetMinValue(inPresentationViewport.m_X, inPresentationViewport.m_Width, + left, m_Layer->m_LeftUnits); + QT3DSF32 horzWidth = GetValueLen(inPresentationViewport.m_Width, width, m_Layer->m_WidthUnits); + QT3DSF32 horzMax = GetMaxValue(inPresentationViewport.m_X, inPresentationViewport.m_Width, + right, m_Layer->m_RightUnits); + + switch (inLayer.m_HorizontalFieldValues) { + case HorizontalFieldValues::LeftWidth: + m_Viewport.m_X = horzMin; + m_Viewport.m_Width = horzWidth; + break; + case HorizontalFieldValues::LeftRight: + m_Viewport.m_X = horzMin; + m_Viewport.m_Width = horzMax - horzMin; + break; + case HorizontalFieldValues::WidthRight: + m_Viewport.m_Width = horzWidth; + m_Viewport.m_X = horzMax - horzWidth; + break; + } + } + { + QT3DSF32 top = m_Layer->m_Top; + QT3DSF32 bottom = m_Layer->m_Bottom; + QT3DSF32 height = m_Layer->m_Height; + + if (m_ScaleMode == qt3ds::render::ScaleModes::FitSelected) { + + if (m_Layer->m_TopUnits == LayerUnitTypes::Pixels) + top *= m_ScaleFactor.y; + + if (m_Layer->m_BottomUnits == LayerUnitTypes::Pixels) + bottom *= m_ScaleFactor.y; + + if (m_Layer->m_HeightUnits == LayerUnitTypes::Pixels) + height *= m_ScaleFactor.y; + } + + QT3DSF32 vertMin = GetMinValue(inPresentationViewport.m_Y, inPresentationViewport.m_Height, + bottom, m_Layer->m_BottomUnits); + QT3DSF32 vertWidth = + GetValueLen(inPresentationViewport.m_Height, height, m_Layer->m_HeightUnits); + QT3DSF32 vertMax = GetMaxValue(inPresentationViewport.m_Y, inPresentationViewport.m_Height, + top, m_Layer->m_TopUnits); + + switch (inLayer.m_VerticalFieldValues) { + case VerticalFieldValues::HeightBottom: + m_Viewport.m_Y = vertMin; + m_Viewport.m_Height = vertWidth; + break; + case VerticalFieldValues::TopBottom: + m_Viewport.m_Y = vertMin; + m_Viewport.m_Height = vertMax - vertMin; + break; + case VerticalFieldValues::TopHeight: + m_Viewport.m_Height = vertWidth; + m_Viewport.m_Y = vertMax - vertWidth; + break; + } + } + + m_Viewport.m_Width = NVMax(1.0f, m_Viewport.m_Width); + m_Viewport.m_Height = NVMax(1.0f, m_Viewport.m_Height); + // Now force the viewport to be a multiple of four in width and height. This is because + // when rendering to a texture we have to respect this and not forcing it causes scaling issues + // that are noticeable especially in situations where customers are using text and such. + QT3DSF32 originalWidth = m_Viewport.m_Width; + QT3DSF32 originalHeight = m_Viewport.m_Height; + + m_Viewport.m_Width = (QT3DSF32)ITextRenderer::NextMultipleOf4((QT3DSU32)m_Viewport.m_Width); + m_Viewport.m_Height = (QT3DSF32)ITextRenderer::NextMultipleOf4((QT3DSU32)m_Viewport.m_Height); + + // Now fudge the offsets to account for this slight difference + m_Viewport.m_X += (originalWidth - m_Viewport.m_Width) / 2.0f; + m_Viewport.m_Y += (originalHeight - m_Viewport.m_Height) / 2.0f; + + m_Scissor = m_Viewport; + m_Scissor.EnsureInBounds(inPresentationScissor); + QT3DS_ASSERT(m_Scissor.m_Width >= 0.0f); + QT3DS_ASSERT(m_Scissor.m_Height >= 0.0f); +} + +// This is the viewport the camera will use to setup the projection. +NVRenderRectF SLayerRenderHelper::GetLayerRenderViewport() const +{ + if (m_Offscreen) + return NVRenderRectF(0, 0, m_Viewport.m_Width, (QT3DSF32)m_Viewport.m_Height); + else + return m_Viewport; +} + +QSize SLayerRenderHelper::GetTextureDimensions() const +{ + QT3DSU32 width = (QT3DSU32)m_Viewport.m_Width; + QT3DSU32 height = (QT3DSU32)m_Viewport.m_Height; + return QSize(ITextRenderer::NextMultipleOf4(width), + ITextRenderer::NextMultipleOf4(height)); +} + +SCameraGlobalCalculationResult SLayerRenderHelper::SetupCameraForRender(SCamera &inCamera) +{ + m_Camera = &inCamera; + NVRenderRectF rect = GetLayerRenderViewport(); + if (m_ScaleMode == ScaleModes::FitSelected) { + rect.m_Width = + (QT3DSF32)(ITextRenderer::NextMultipleOf4((QT3DSU32)(rect.m_Width / m_ScaleFactor.x))); + rect.m_Height = + (QT3DSF32)(ITextRenderer::NextMultipleOf4((QT3DSU32)(rect.m_Height / m_ScaleFactor.y))); + } + return m_Camera->CalculateGlobalVariables(rect, m_PresentationDesignDimensions); +} + +Option<QT3DSVec2> SLayerRenderHelper::GetLayerMouseCoords(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect) const +{ + // First invert the y so we are dealing with numbers in a normal coordinate space. + // Second, move into our layer's coordinate space + QT3DSVec2 correctCoords(inMouseCoords.x, inWindowDimensions.y - inMouseCoords.y); + QT3DSVec2 theLocalMouse = m_Viewport.ToRectRelative(correctCoords); + + QT3DSF32 theRenderRectWidth = m_Viewport.m_Width; + QT3DSF32 theRenderRectHeight = m_Viewport.m_Height; + // Crop the mouse to the rect. Apply no further translations. + if (inForceIntersect == false + && (theLocalMouse.x < 0.0f || theLocalMouse.x >= theRenderRectWidth + || theLocalMouse.y < 0.0f || theLocalMouse.y >= theRenderRectHeight)) { + return Empty(); + } + return theLocalMouse; +} + +Option<SRay> SLayerRenderHelper::GetPickRay(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect, + bool sceneCameraView) const +{ + if (m_Camera == NULL) + return Empty(); + Option<QT3DSVec2> theCoords( + GetLayerMouseCoords(inMouseCoords, inWindowDimensions, inForceIntersect)); + if (theCoords.hasValue()) { + // The cameras projection is different if we are onscreen vs. offscreen. + // When offscreen, we need to move the mouse coordinates into a local space + // to the layer. + return m_Camera->Unproject(*theCoords, m_Viewport, m_PresentationDesignDimensions, + sceneCameraView); + } + return Empty(); +} + +bool SLayerRenderHelper::IsLayerVisible() const +{ + return m_Scissor.m_Height >= 2.0f && m_Scissor.m_Width >= 2.0f; +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h new file mode 100644 index 0000000..616ebe1 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderContextCore.h" + +namespace qt3ds { +namespace render { + + /** An independent, testable entity to encapsulate taking at least: + * layer, current viewport rect, current scissor rect, presentation design dimensions + * and producing a set of rectangles: + * layer viewport rect (inside viewport rect and calculated using outer viewport rect info) + * layer scissor rect (inside current scissor rect) + * layer camera rect (may be the viewport rect) + * + * In the case where we have to render offscreen for this layer then we need to handle produce + * a set of texture dimensions and the layer camera rect ends up being same size but with no + *offsets. + * + * This object should handle part of any translation from screenspace to global space. + * I am using language level access control on this object because it needs specific + * interface design that will enable future modifications. + */ + struct SLayerRenderHelper + { + private: + NVRenderRectF m_PresentationViewport; + NVRenderRectF m_PresentationScissor; + QT3DSVec2 m_PresentationDesignDimensions; + SLayer *m_Layer; + SCamera *m_Camera; + bool m_Offscreen; + + NVRenderRectF m_Viewport; + NVRenderRectF m_Scissor; + + ScaleModes::Enum m_ScaleMode; + QT3DSVec2 m_ScaleFactor; + + public: + SLayerRenderHelper(); + + SLayerRenderHelper(const NVRenderRectF &inPresentationViewport, + const NVRenderRectF &inPresentationScissor, + const QT3DSVec2 &inPresentationDesignDimensions, SLayer &inLayer, + bool inOffscreen, qt3ds::render::ScaleModes::Enum inScaleMode, + qt3ds::QT3DSVec2 inScaleFactor); + + NVRenderRectF GetPresentationViewport() const { return m_PresentationViewport; } + NVRenderRectF GetPresentationScissor() const { return m_PresentationScissor; } + QT3DSVec2 GetPresentationDesignDimensions() const { return m_PresentationDesignDimensions; } + SLayer *GetLayer() const { return m_Layer; } + SCamera *GetCamera() const { return m_Camera; } + bool IsOffscreen() const { return m_Offscreen; } + + // Does not differ whether offscreen or not, simply states how this layer maps to the + // presentation + NVRenderRectF GetLayerToPresentationViewport() const { return m_Viewport; } + // Does not differ whether offscreen or not, scissor rect of how this layer maps to + // presentation. + NVRenderRectF GetLayerToPresentationScissorRect() const { return m_Scissor; } + + QSize GetTextureDimensions() const; + + SCameraGlobalCalculationResult SetupCameraForRender(SCamera &inCamera); + + Option<QT3DSVec2> GetLayerMouseCoords(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect) const; + + Option<SRay> GetPickRay(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect, bool sceneCameraView = false) const; + + // Checks the various viewports and determines if the layer is visible or not. + bool IsLayerVisible() const; + + private: + // Viewport used when actually rendering. In the case where this is an offscreen item then + // it may be + // different than the layer to presentation viewport. + NVRenderRectF GetLayerRenderViewport() const; + }; +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp new file mode 100644 index 0000000..8afa3c0 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp @@ -0,0 +1,1477 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderEffect.h" +#include "EASTL/sort.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderPresentation.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderResourceManager.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderEffectSystem.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderResourceBufferObjects.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathManager.h" + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif +namespace qt3ds { +namespace render { + using eastl::reverse; + using eastl::stable_sort; + using qt3ds::render::NVRenderContextScopedProperty; + using qt3ds::QT3DSVec2; + + namespace { + void MaybeQueueNodeForRender(SNode &inNode, nvvector<SRenderableNodeEntry> &outRenderables, + nvvector<SNode *> &outCamerasAndLights, QT3DSU32 &ioDFSIndex) + { + ++ioDFSIndex; + inNode.m_DFSIndex = ioDFSIndex; + if (GraphObjectTypes::IsRenderableType(inNode.m_Type)) + outRenderables.push_back(inNode); + else if (GraphObjectTypes::IsLightCameraType(inNode.m_Type)) + outCamerasAndLights.push_back(&inNode); + + for (SNode *theChild = inNode.m_FirstChild; theChild != NULL; + theChild = theChild->m_NextSibling) + MaybeQueueNodeForRender(*theChild, outRenderables, outCamerasAndLights, ioDFSIndex); + } + + bool HasValidLightProbe(SImage *inLightProbeImage) + { + return inLightProbeImage && inLightProbeImage->m_TextureData.m_Texture; + } + } + + SDefaultMaterialPreparationResult::SDefaultMaterialPreparationResult( + SShaderDefaultMaterialKey inKey) + : m_FirstImage(NULL) + , m_Opacity(1.0f) + , m_MaterialKey(inKey) + , m_Dirty(false) + { + } + +#define MAX_AA_LEVELS 8 + + SLayerRenderPreparationData::SLayerRenderPreparationData(SLayer &inLayer, + Qt3DSRendererImpl &inRenderer) + : m_Layer(inLayer) + , m_Renderer(inRenderer) + , m_Allocator(inRenderer.GetContext().GetAllocator()) + , m_RenderableNodeLightEntryPool( + ForwardingAllocator(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderableNodes")) + , m_RenderableNodes(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderableNodes") + , m_LightToNodeMap(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_LightToNodeMap") + , m_CamerasAndLights(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_CamerasAndLights") + , m_Camera(NULL) + , m_Lights(inRenderer.GetContext().GetAllocator(), "SLayerRenderPreparationData::m_Lights") + , m_OpaqueObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_OpaqueObjects") + , m_TransparentObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_TransparentObjects") + , m_RenderedOpaqueObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderedOpaqueObjects") + , m_RenderedTransparentObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderedTransparentObjects") + , m_IRenderWidgets(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_IRenderWidgets") + , m_SourceLightDirections(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_SourceLightDirections") + , m_LightDirections(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_LightDirections") + , m_ModelContexts(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_ModelContexts") + , m_CGLightingFeatureName( + inRenderer.GetContext().GetStringTable().RegisterStr("QT3DS_ENABLE_CG_LIGHTING")) + , m_FeaturesDirty(true) + , m_FeatureSetHash(0) + , m_TooManyLightsError(false) + { + } + + SLayerRenderPreparationData::~SLayerRenderPreparationData() {} + + bool SLayerRenderPreparationData::NeedsWidgetTexture() const + { + return m_IRenderWidgets.size() > 0; + } + + void SLayerRenderPreparationData::SetShaderFeature(CRegisteredString theStr, bool inValue) + { + SShaderPreprocessorFeature item(theStr, inValue); + eastl::vector<SShaderPreprocessorFeature>::iterator iter = m_Features.begin(), + end = m_Features.end(); + + // empty loop intentional. + for (; iter != end && ((iter->m_Name == theStr) == false); ++iter) + ; + + if (iter != end) { + if (iter->m_Enabled != inValue) { + iter->m_Enabled = inValue; + m_FeaturesDirty = true; + m_FeatureSetHash = 0; + } + } else { + m_Features.push_back(item); + m_FeaturesDirty = true; + m_FeatureSetHash = 0; + } + } + + void SLayerRenderPreparationData::SetShaderFeature(const char *inName, bool inValue) + { + CRegisteredString theStr(m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(inName)); + SetShaderFeature(theStr, inValue); + } + + NVConstDataRef<SShaderPreprocessorFeature> SLayerRenderPreparationData::GetShaderFeatureSet() + { + if (m_FeaturesDirty) { + eastl::sort(m_Features.begin(), m_Features.end()); + m_FeaturesDirty = false; + } + return toConstDataRef(m_Features.data(), (QT3DSU32)m_Features.size()); + } + + size_t SLayerRenderPreparationData::GetShaderFeatureSetHash() + { + if (!m_FeatureSetHash) + m_FeatureSetHash = HashShaderFeatureSet(GetShaderFeatureSet()); + return m_FeatureSetHash; + } + + bool SLayerRenderPreparationData::GetShadowMapManager() + { + if (m_ShadowMapManager.mPtr) + return true; + + m_ShadowMapManager.mPtr = Qt3DSShadowMap::Create(m_Renderer.GetQt3DSContext()); + + return m_ShadowMapManager.mPtr != NULL; + } + + bool SLayerRenderPreparationData::GetOffscreenRenderer() + { + if (m_LastFrameOffscreenRenderer.mPtr) + return true; + + if (m_Layer.m_RenderPlugin && m_Layer.m_RenderPlugin->m_Flags.IsActive()) { + IRenderPluginInstance *theInstance = + m_Renderer.GetQt3DSContext().GetRenderPluginManager().GetOrCreateRenderPluginInstance( + m_Layer.m_RenderPlugin->m_PluginPath, m_Layer.m_RenderPlugin); + if (theInstance) { + m_Renderer.GetQt3DSContext() + .GetOffscreenRenderManager() + .MaybeRegisterOffscreenRenderer(&theInstance, *theInstance); + m_LastFrameOffscreenRenderer = theInstance; + } + } + if (m_LastFrameOffscreenRenderer.mPtr == NULL) + m_LastFrameOffscreenRenderer = + m_Renderer.GetQt3DSContext().GetOffscreenRenderManager().GetOffscreenRenderer( + m_Layer.m_TexturePath); + return m_LastFrameOffscreenRenderer.mPtr != NULL; + } + + QT3DSVec3 SLayerRenderPreparationData::GetCameraDirection() + { + if (m_CameraDirection.hasValue() == false) { + if (m_Camera) + m_CameraDirection = m_Camera->GetScalingCorrectDirection(); + else + m_CameraDirection = QT3DSVec3(0, 0, -1); + } + return *m_CameraDirection; + } + + // Per-frame cache of renderable objects post-sort. + NVDataRef<SRenderableObject *> SLayerRenderPreparationData::GetOpaqueRenderableObjects() + { + if (m_RenderedOpaqueObjects.empty() == false || m_Camera == NULL) + return m_RenderedOpaqueObjects; + if (m_Layer.m_Flags.IsLayerEnableDepthTest() && m_OpaqueObjects.empty() == false) { + QT3DSVec3 theCameraDirection(GetCameraDirection()); + QT3DSVec3 theCameraPosition = m_Camera->GetGlobalPos(); + m_RenderedOpaqueObjects.assign(m_OpaqueObjects.begin(), m_OpaqueObjects.end()); + // Setup the object's sorting information + for (QT3DSU32 idx = 0, end = m_RenderedOpaqueObjects.size(); idx < end; ++idx) { + SRenderableObject &theInfo = *m_RenderedOpaqueObjects[idx]; + QT3DSVec3 difference = theInfo.m_WorldCenterPoint - theCameraPosition; + theInfo.m_CameraDistanceSq = difference.dot(theCameraDirection); + } + + ForwardingAllocator alloc(m_Renderer.GetPerFrameAllocator(), "SortAllocations"); + // Render nearest to furthest objects + eastl::merge_sort(m_RenderedOpaqueObjects.begin(), m_RenderedOpaqueObjects.end(), alloc, + ISRenderObjectPtrLessThan); + } + return m_RenderedOpaqueObjects; + } + + // If layer depth test is false, this may also contain opaque objects. + NVDataRef<SRenderableObject *> SLayerRenderPreparationData::GetTransparentRenderableObjects() + { + if (m_RenderedTransparentObjects.empty() == false || m_Camera == NULL) + return m_RenderedTransparentObjects; + + m_RenderedTransparentObjects.assign(m_TransparentObjects.begin(), + m_TransparentObjects.end()); + + if (m_Layer.m_Flags.IsLayerEnableDepthTest() == false) + m_RenderedTransparentObjects.insert(m_RenderedTransparentObjects.end(), + m_OpaqueObjects.begin(), m_OpaqueObjects.end()); + + if (m_RenderedTransparentObjects.empty() == false) { + QT3DSVec3 theCameraDirection(GetCameraDirection()); + QT3DSVec3 theCameraPosition = m_Camera->GetGlobalPos(); + + // Setup the object's sorting information + for (QT3DSU32 idx = 0, end = m_RenderedTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theInfo = *m_RenderedTransparentObjects[idx]; + QT3DSVec3 difference = theInfo.m_WorldCenterPoint - theCameraPosition; + theInfo.m_CameraDistanceSq = difference.dot(theCameraDirection); + } + ForwardingAllocator alloc(m_Renderer.GetPerFrameAllocator(), "SortAllocations"); + // render furthest to nearest. + eastl::merge_sort(m_RenderedTransparentObjects.begin(), + m_RenderedTransparentObjects.end(), alloc, + ISRenderObjectPtrGreatThan); + } + + return m_RenderedTransparentObjects; + } + +#define MAX_LAYER_WIDGETS 200 + + void SLayerRenderPreparationData::AddRenderWidget(IRenderWidget &inWidget) + { + // The if the layer is not active then the widget can't be displayed. + // Furthermore ResetForFrame won't be called below which leads to stale + // widgets in the m_IRenderWidgets array. These stale widgets would get rendered + // the next time the layer was active potentially causing a crash. + if (!m_Layer.m_Flags.IsActive()) + return; + + // Ensure we clear the widget layer always + m_Renderer.LayerNeedsFrameClear(*static_cast<SLayerRenderData *>(this)); + + if (m_IRenderWidgets.size() < MAX_LAYER_WIDGETS) + m_IRenderWidgets.push_back(&inWidget); + } + +#define RENDER_FRAME_NEW(type) QT3DS_NEW(m_Renderer.GetPerFrameAllocator(), type) + +#define QT3DS_RENDER_MINIMUM_RENDER_OPACITY .01f + + SShaderDefaultMaterialKey + SLayerRenderPreparationData::GenerateLightingKey(DefaultMaterialLighting::Enum inLightingType) + { + SShaderDefaultMaterialKey theGeneratedKey(GetShaderFeatureSetHash()); + const bool lighting = inLightingType != DefaultMaterialLighting::NoLighting; + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasLighting.SetValue(theGeneratedKey, + lighting); + if (lighting) { + const bool lightProbe = m_Layer.m_LightProbe + && m_Layer.m_LightProbe->m_TextureData.m_Texture; + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.SetValue(theGeneratedKey, + lightProbe); + + QT3DSU32 numLights = (QT3DSU32)m_Lights.size(); + if (numLights > SShaderDefaultMaterialKeyProperties::LightCount + && m_TooManyLightsError == false) { + m_TooManyLightsError = true; + numLights = SShaderDefaultMaterialKeyProperties::LightCount; + qCCritical(INVALID_OPERATION, "Too many lights on layer, max is 7"); + QT3DS_ASSERT(false); + } + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightCount.SetValue(theGeneratedKey, + numLights); + + for (QT3DSU32 lightIdx = 0, lightEnd = m_Lights.size(); + lightIdx < lightEnd; ++lightIdx) { + SLight *theLight(m_Lights[lightIdx]); + const bool isDirectional = theLight->m_LightType == RenderLightTypes::Directional; + const bool isArea = theLight->m_LightType == RenderLightTypes::Area; + const bool castShadowsArea = (theLight->m_LightType != RenderLightTypes::Area) + && (theLight->m_CastShadow); + + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightFlags[lightIdx] + .SetValue(theGeneratedKey, !isDirectional); + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightAreaFlags[lightIdx] + .SetValue(theGeneratedKey, isArea); + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightShadowFlags[lightIdx] + .SetValue(theGeneratedKey, castShadowsArea); + } + } + return theGeneratedKey; + } + + bool SLayerRenderPreparationData::PrepareTextForRender( + SText &inText, const QT3DSMat44 &inViewProjection, + QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags) + { + ITextTextureCache *theTextRenderer = m_Renderer.GetQt3DSContext().GetTextureCache(); + if (theTextRenderer == NULL) + return false; + + SRenderableObjectFlags theFlags; + theFlags.SetHasTransparency(true); + theFlags.SetCompletelyTransparent(inText.m_GlobalOpacity < .01f); + theFlags.SetPickable(true); + bool retval = false; + + if (theFlags.IsCompletelyTransparent() == false) { + retval = inText.m_Flags.IsDirty() || inText.m_Flags.IsTextDirty(); + inText.m_Flags.SetTextDirty(false); + QT3DSMat44 theMVP; + QT3DSMat33 theNormalMatrix; + inText.CalculateMVPAndNormalMatrix(inViewProjection, theMVP, theNormalMatrix); + + SRenderableObject *theRenderable = nullptr; + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + Q3DSDistanceFieldRenderer *distanceFieldText + = static_cast<Q3DSDistanceFieldRenderer *>( + m_Renderer.GetQt3DSContext().getDistanceFieldRenderer()); + theRenderable = RENDER_FRAME_NEW(SDistanceFieldRenderable)( + theFlags, inText.GetGlobalPos(), inText, inText.m_Bounds, theMVP, + *distanceFieldText); +#else + 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); + + theRenderable = RENDER_FRAME_NEW(STextRenderable)( + theFlags, inText.GetGlobalPos(), m_Renderer, inText, inText.m_Bounds, theMVP, + inViewProjection, *inText.m_TextTexture, theTextOffset, theTextScale); +#endif + // After preparation, do not push object back to queue if it is not + // active, because we prepare text elements regardless of their + // visibility (=active status). + if (inText.m_Flags.IsGloballyActive()) + m_TransparentObjects.push_back(theRenderable); + } + return retval; + } + + eastl::pair<bool, SGraphObject *> + SLayerRenderPreparationData::ResolveReferenceMaterial(SGraphObject *inMaterial) + { + bool subsetDirty = false; + bool badIdea = false; + SGraphObject *theSourceMaterialObject(inMaterial); + SGraphObject *theMaterialObject(inMaterial); + while (theMaterialObject + && theMaterialObject->m_Type == GraphObjectTypes::ReferencedMaterial && !badIdea) { + SReferencedMaterial *theRefMaterial = + static_cast<SReferencedMaterial *>(theMaterialObject); + theMaterialObject = theRefMaterial->m_ReferencedMaterial; + if (theMaterialObject == theSourceMaterialObject) { + badIdea = true; + } + + if (theRefMaterial == theSourceMaterialObject) { + theRefMaterial->m_Dirty.UpdateDirtyForFrame(); + } + subsetDirty = subsetDirty | theRefMaterial->m_Dirty.IsDirty(); + } + if (badIdea) { + theMaterialObject = NULL; + } + return eastl::make_pair(subsetDirty, theMaterialObject); + } + + bool SLayerRenderPreparationData::PreparePathForRender( + SPath &inPath, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, SLayerRenderPreparationResultFlags &ioFlags) + { + SRenderableObjectFlags theSharedFlags; + theSharedFlags.SetPickable(true); + QT3DSF32 subsetOpacity = inPath.m_GlobalOpacity; + bool retval = inPath.m_Flags.IsDirty(); + inPath.m_Flags.SetDirty(false); + QT3DSMat44 theMVP; + QT3DSMat33 theNormalMatrix; + + inPath.CalculateMVPAndNormalMatrix(inViewProjection, theMVP, theNormalMatrix); + NVBounds3 theBounds(this->m_Renderer.GetQt3DSContext().GetPathManager().GetBounds(inPath)); + + if (inPath.m_GlobalOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY + && inClipFrustum.hasValue()) { + // Check bounding box against the clipping planes + NVBounds3 theGlobalBounds = theBounds; + theGlobalBounds.transform(inPath.m_GlobalTransform); + if (inClipFrustum->intersectsWith(theGlobalBounds) == false) + subsetOpacity = 0.0f; + } + + SGraphObject *theMaterials[2] = { inPath.m_Material, inPath.m_SecondMaterial }; + + if (inPath.m_PathType == PathTypes::Geometry + || inPath.m_PaintStyle != PathPaintStyles::FilledAndStroked) + theMaterials[1] = NULL; + + // We need to fill material to be the first one rendered so the stroke goes on top. + // In the timeline, however, this is reversed. + + if (theMaterials[1]) + eastl::swap(theMaterials[1], theMaterials[0]); + + for (QT3DSU32 idx = 0, end = 2; idx < end; ++idx) { + if (theMaterials[idx] == NULL) + continue; + + SRenderableObjectFlags theFlags = theSharedFlags; + + eastl::pair<bool, SGraphObject *> theMaterialAndDirty( + ResolveReferenceMaterial(theMaterials[idx])); + SGraphObject *theMaterial(theMaterialAndDirty.second); + retval = retval || theMaterialAndDirty.first; + + if (theMaterial != NULL && theMaterial->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theDefaultMaterial = static_cast<SDefaultMaterial *>(theMaterial); + // Don't clear dirty flags if the material was referenced. + bool clearMaterialFlags = theMaterial == inPath.m_Material; + SDefaultMaterialPreparationResult prepResult(PrepareDefaultMaterialForRender( + *theDefaultMaterial, theFlags, subsetOpacity, clearMaterialFlags)); + + theFlags = prepResult.m_RenderableFlags; + if (inPath.m_PathType == PathTypes::Geometry) { + if ((inPath.m_BeginCapping != PathCapping::Noner + && inPath.m_BeginCapOpacity < 1.0f) + || (inPath.m_EndCapping != PathCapping::Noner + && inPath.m_EndCapOpacity < 1.0f)) + theFlags.SetHasTransparency(true); + } else { + ioFlags.SetRequiresStencilBuffer(true); + } + retval = retval || prepResult.m_Dirty; + bool isStroke = true; + if (idx == 0 && inPath.m_PathType == PathTypes::Painted) { + if (inPath.m_PaintStyle == PathPaintStyles::Filled + || inPath.m_PaintStyle == PathPaintStyles::FilledAndStroked) + isStroke = false; + } + + SPathRenderable *theRenderable = RENDER_FRAME_NEW(SPathRenderable)( + theFlags, inPath.GetGlobalPos(), m_Renderer, inPath.m_GlobalTransform, + theBounds, inPath, theMVP, theNormalMatrix, *theMaterial, prepResult.m_Opacity, + prepResult.m_MaterialKey, isStroke); + theRenderable->m_FirstImage = prepResult.m_FirstImage; + + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IPathManager &thePathManager = qt3dsContext.GetPathManager(); + retval = thePathManager.PrepareForRender(inPath) || retval; + retval |= (inPath.m_WireframeMode != qt3dsContext.GetWireframeMode()); + inPath.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + if (theFlags.HasTransparency()) + m_TransparentObjects.push_back(theRenderable); + else + m_OpaqueObjects.push_back(theRenderable); + } else if (theMaterial != NULL + && theMaterial->m_Type == GraphObjectTypes::CustomMaterial) { + SCustomMaterial *theCustomMaterial = static_cast<SCustomMaterial *>(theMaterial); + // Don't clear dirty flags if the material was referenced. + // bool clearMaterialFlags = theMaterial == inPath.m_Material; + SDefaultMaterialPreparationResult prepResult( + PrepareCustomMaterialForRender(*theCustomMaterial, theFlags, subsetOpacity)); + + theFlags = prepResult.m_RenderableFlags; + if (inPath.m_PathType == PathTypes::Geometry) { + if ((inPath.m_BeginCapping != PathCapping::Noner + && inPath.m_BeginCapOpacity < 1.0f) + || (inPath.m_EndCapping != PathCapping::Noner + && inPath.m_EndCapOpacity < 1.0f)) + theFlags.SetHasTransparency(true); + } else { + ioFlags.SetRequiresStencilBuffer(true); + } + + retval = retval || prepResult.m_Dirty; + bool isStroke = true; + if (idx == 0 && inPath.m_PathType == PathTypes::Painted) { + if (inPath.m_PaintStyle == PathPaintStyles::Filled + || inPath.m_PaintStyle == PathPaintStyles::FilledAndStroked) + isStroke = false; + } + + SPathRenderable *theRenderable = RENDER_FRAME_NEW(SPathRenderable)( + theFlags, inPath.GetGlobalPos(), m_Renderer, inPath.m_GlobalTransform, + theBounds, inPath, theMVP, theNormalMatrix, *theMaterial, prepResult.m_Opacity, + prepResult.m_MaterialKey, isStroke); + theRenderable->m_FirstImage = prepResult.m_FirstImage; + + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IPathManager &thePathManager = qt3dsContext.GetPathManager(); + retval = thePathManager.PrepareForRender(inPath) || retval; + retval |= (inPath.m_WireframeMode != qt3dsContext.GetWireframeMode()); + inPath.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + if (theFlags.HasTransparency()) + m_TransparentObjects.push_back(theRenderable); + else + m_OpaqueObjects.push_back(theRenderable); + } + } + return retval; + } + + void SLayerRenderPreparationData::PrepareImageForRender( + SImage &inImage, ImageMapTypes::Enum inMapType, SRenderableImage *&ioFirstImage, + SRenderableImage *&ioNextImage, SRenderableObjectFlags &ioFlags, + SShaderDefaultMaterialKey &inShaderKey, QT3DSU32 inImageIndex) + { + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IBufferManager &bufferManager = qt3dsContext.GetBufferManager(); + IOffscreenRenderManager &theOffscreenRenderManager( + qt3dsContext.GetOffscreenRenderManager()); + IRenderPluginManager &theRenderPluginManager(qt3dsContext.GetRenderPluginManager()); + if (inImage.ClearDirty(bufferManager, theOffscreenRenderManager, theRenderPluginManager)) + ioFlags |= RenderPreparationResultFlagValues::Dirty; + + // All objects with offscreen renderers are pickable so we can pass the pick through to the + // offscreen renderer and let it deal with the pick. + if (inImage.m_LastFrameOffscreenRenderer != NULL) { + ioFlags.SetPickable(true); + ioFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + + if (inImage.m_TextureData.m_Texture) { + if (inImage.m_TextureData.m_TextureFlags.HasTransparency() + && (inMapType == ImageMapTypes::Diffuse + || inMapType == ImageMapTypes::Opacity + || inMapType == ImageMapTypes::Translucency)) { + ioFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + // Textures used in general have linear characteristics. + // PKC -- The filters are properly set already. Setting them here only overrides what + // would + // otherwise be a correct setting. + // inImage.m_TextureData.m_Texture->SetMinFilter( NVRenderTextureMinifyingOp::Linear ); + // inImage.m_TextureData.m_Texture->SetMagFilter( NVRenderTextureMagnifyingOp::Linear ); + + SRenderableImage *theImage = RENDER_FRAME_NEW(SRenderableImage)(inMapType, inImage); + SShaderKeyImageMap &theKeyProp = + m_Renderer.DefaultMaterialShaderKeyProperties().m_ImageMaps[inImageIndex]; + + theKeyProp.SetEnabled(inShaderKey, true); + switch (inImage.m_MappingMode) { + default: + QT3DS_ASSERT(false); + // fallthrough intentional + case ImageMappingModes::Normal: + break; + case ImageMappingModes::Environment: + theKeyProp.SetEnvMap(inShaderKey, true); + break; + case ImageMappingModes::LightProbe: + theKeyProp.SetLightProbe(inShaderKey, true); + break; + } + + if (inImage.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + theKeyProp.SetInvertUVMap(inShaderKey, true); + if (ioFirstImage == NULL) + ioFirstImage = theImage; + else + ioNextImage->m_NextImage = theImage; + + if (inImage.m_TextureData.m_TextureFlags.IsPreMultiplied()) + theKeyProp.SetPremultiplied(inShaderKey, true); + + SShaderKeyTextureSwizzle &theSwizzleKeyProp = + m_Renderer.DefaultMaterialShaderKeyProperties().m_TextureSwizzle[inImageIndex]; + theSwizzleKeyProp.SetSwizzleMode( + inShaderKey, inImage.m_TextureData.m_Texture->GetTextureSwizzleMode(), true); + + ioNextImage = theImage; + } + } + + SDefaultMaterialPreparationResult SLayerRenderPreparationData::PrepareDefaultMaterialForRender( + SDefaultMaterial &inMaterial, SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity, + bool inClearDirtyFlags) + { + SDefaultMaterial *theMaterial = &inMaterial; + SDefaultMaterialPreparationResult retval(GenerateLightingKey(theMaterial->m_Lighting)); + retval.m_RenderableFlags = inExistingFlags; + SRenderableObjectFlags &renderableFlags(retval.m_RenderableFlags); + SShaderDefaultMaterialKey &theGeneratedKey(retval.m_MaterialKey); + retval.m_Opacity = inOpacity; + QT3DSF32 &subsetOpacity(retval.m_Opacity); + + if (theMaterial->m_Dirty.IsDirty()) { + renderableFlags |= RenderPreparationResultFlagValues::Dirty; + } + subsetOpacity *= theMaterial->m_Opacity; + if (inClearDirtyFlags) + theMaterial->m_Dirty.UpdateDirtyForFrame(); + + SRenderableImage *firstImage = NULL; + + // set wireframe mode + m_Renderer.DefaultMaterialShaderKeyProperties().m_WireframeMode.SetValue( + theGeneratedKey, m_Renderer.GetQt3DSContext().GetWireframeMode()); + + if (theMaterial->m_IblProbe && CheckLightProbeDirty(*theMaterial->m_IblProbe)) { + m_Renderer.PrepareImageForIbl(*theMaterial->m_IblProbe); + } + + if (!m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.GetValue(theGeneratedKey)) { + bool lightProbeValid = HasValidLightProbe(theMaterial->m_IblProbe); + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", lightProbeValid); + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.SetValue(theGeneratedKey, + lightProbeValid); + // SetShaderFeature( "QT3DS_ENABLE_IBL_FOV", + // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f ); + } + + if (subsetOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + + if (theMaterial->m_BlendMode != DefaultMaterialBlendMode::Normal + || theMaterial->m_OpacityMap) { + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + + bool specularEnabled = theMaterial->IsSpecularEnabled(); + m_Renderer.DefaultMaterialShaderKeyProperties().m_SpecularEnabled.SetValue( + theGeneratedKey, specularEnabled); + if (specularEnabled) { + m_Renderer.DefaultMaterialShaderKeyProperties().m_SpecularModel.SetSpecularModel( + theGeneratedKey, theMaterial->m_SpecularModel); + } + + m_Renderer.DefaultMaterialShaderKeyProperties().m_FresnelEnabled.SetValue( + theGeneratedKey, theMaterial->IsFresnelEnabled()); + + m_Renderer.DefaultMaterialShaderKeyProperties().m_VertexColorsEnabled.SetValue( + theGeneratedKey, theMaterial->IsVertexColorsEnabled()); + + // Run through the material's images and prepare them for render. + // this may in fact set pickable on the renderable flags if one of the images + // links to a sub presentation or any offscreen rendered object. + SRenderableImage *nextImage = NULL; +#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \ + if ((img)) \ + PrepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \ + theGeneratedKey, shadercomponent); + + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[0], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap0); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[1], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap1); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[2], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap2); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_EmissiveMap, ImageMapTypes::Emissive, + SShaderDefaultMaterialKeyProperties::EmissiveMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_EmissiveMap2, ImageMapTypes::Emissive, + SShaderDefaultMaterialKeyProperties::EmissiveMap2); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_SpecularReflection, ImageMapTypes::Specular, + SShaderDefaultMaterialKeyProperties::SpecularMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_RoughnessMap, ImageMapTypes::Roughness, + SShaderDefaultMaterialKeyProperties::RoughnessMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_OpacityMap, ImageMapTypes::Opacity, + SShaderDefaultMaterialKeyProperties::OpacityMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_BumpMap, ImageMapTypes::Bump, + SShaderDefaultMaterialKeyProperties::BumpMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_SpecularMap, ImageMapTypes::SpecularAmountMap, + SShaderDefaultMaterialKeyProperties::SpecularAmountMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_NormalMap, ImageMapTypes::Normal, + SShaderDefaultMaterialKeyProperties::NormalMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DisplacementMap, ImageMapTypes::Displacement, + SShaderDefaultMaterialKeyProperties::DisplacementMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_TranslucencyMap, ImageMapTypes::Translucency, + SShaderDefaultMaterialKeyProperties::TranslucencyMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapIndirect, + ImageMapTypes::LightmapIndirect, + SShaderDefaultMaterialKeyProperties::LightmapIndirect); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapRadiosity, + ImageMapTypes::LightmapRadiosity, + SShaderDefaultMaterialKeyProperties::LightmapRadiosity); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapShadow, + ImageMapTypes::LightmapShadow, + SShaderDefaultMaterialKeyProperties::LightmapShadow); + } +#undef CHECK_IMAGE_AND_PREPARE + + if (subsetOpacity < QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + subsetOpacity = 0.0f; + // You can still pick against completely transparent objects(or rather their bounding + // box) + // you just don't render them. + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + renderableFlags |= RenderPreparationResultFlagValues::CompletelyTransparent; + } + + if (IsNotOne(subsetOpacity)) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + + retval.m_FirstImage = firstImage; + if (retval.m_RenderableFlags.IsDirty()) + retval.m_Dirty = true; + return retval; + } + + SDefaultMaterialPreparationResult SLayerRenderPreparationData::PrepareCustomMaterialForRender( + SCustomMaterial &inMaterial, SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity) + { + SDefaultMaterialPreparationResult retval(GenerateLightingKey( + DefaultMaterialLighting::FragmentLighting)); // always fragment lighting + retval.m_RenderableFlags = inExistingFlags; + SRenderableObjectFlags &renderableFlags(retval.m_RenderableFlags); + SShaderDefaultMaterialKey &theGeneratedKey(retval.m_MaterialKey); + retval.m_Opacity = inOpacity; + QT3DSF32 &subsetOpacity(retval.m_Opacity); + + // If the custom material uses subpresentations, those have to be rendered before + // the custom material itself + m_Renderer.GetQt3DSContext().GetCustomMaterialSystem().renderSubpresentations(inMaterial); + + // set wireframe mode + m_Renderer.DefaultMaterialShaderKeyProperties().m_WireframeMode.SetValue( + theGeneratedKey, m_Renderer.GetQt3DSContext().GetWireframeMode()); + + if (subsetOpacity < QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + subsetOpacity = 0.0f; + // You can still pick against completely transparent objects(or rather their bounding + // box) + // you just don't render them. + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + renderableFlags |= RenderPreparationResultFlagValues::CompletelyTransparent; + } + + if (IsNotOne(subsetOpacity)) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + + SRenderableImage *firstImage = NULL; + SRenderableImage *nextImage = NULL; + +#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \ + if ((img)) \ + PrepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \ + theGeneratedKey, shadercomponent); + + CHECK_IMAGE_AND_PREPARE(inMaterial.m_DisplacementMap, ImageMapTypes::Displacement, + SShaderDefaultMaterialKeyProperties::DisplacementMap); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapIndirect, + ImageMapTypes::LightmapIndirect, + SShaderDefaultMaterialKeyProperties::LightmapIndirect); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapRadiosity, + ImageMapTypes::LightmapRadiosity, + SShaderDefaultMaterialKeyProperties::LightmapRadiosity); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapShadow, + ImageMapTypes::LightmapShadow, + SShaderDefaultMaterialKeyProperties::LightmapShadow); +#undef CHECK_IMAGE_AND_PREPARE + + retval.m_FirstImage = firstImage; + return retval; + } + + bool SLayerRenderPreparationData::PrepareModelForRender( + SModel &inModel, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, TNodeLightEntryList &inScopedLights) + { + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IBufferManager &bufferManager = qt3dsContext.GetBufferManager(); + SRenderMesh *theMesh = bufferManager.LoadMesh(inModel.m_MeshPath); + if (theMesh == NULL) + return false; + + SGraphObject *theSourceMaterialObject = inModel.m_FirstMaterial; + SModelContext &theModelContext = + *RENDER_FRAME_NEW(SModelContext)(inModel, inViewProjection); + m_ModelContexts.push_back(&theModelContext); + + bool subsetDirty = false; + + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, m_SourceLightDirections, + inScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + for (QT3DSU32 idx = 0, end = theMesh->m_Subsets.size(); idx < end && theSourceMaterialObject; + ++idx, theSourceMaterialObject = GetNextMaterialSibling(theSourceMaterialObject)) { + SRenderSubset &theOuterSubset(theMesh->m_Subsets[idx]); + { + SRenderSubset &theSubset(theOuterSubset); + SRenderableObjectFlags renderableFlags; + renderableFlags.SetPickable(false); + renderableFlags.SetShadowCaster(inModel.m_ShadowCaster); + QT3DSF32 subsetOpacity = inModel.m_GlobalOpacity; + QT3DSVec3 theModelCenter(theSubset.m_Bounds.getCenter()); + theModelCenter = inModel.m_GlobalTransform.transform(theModelCenter); + + if (subsetOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY + && inClipFrustum.hasValue()) { + // Check bounding box against the clipping planes + NVBounds3 theGlobalBounds = theSubset.m_Bounds; + theGlobalBounds.transform(theModelContext.m_Model.m_GlobalTransform); + if (inClipFrustum->intersectsWith(theGlobalBounds) == false) + subsetOpacity = 0.0f; + } + + // For now everything is pickable. Eventually we want to have localPickable and + // globalPickable set on the node during + // updates and have the runtime tell us what is pickable and what is not pickable. + // Completely transparent models cannot be pickable. But models with completely + // transparent materials + // still are. This allows the artist to control pickability in a somewhat + // fine-grained style. + bool canModelBePickable = inModel.m_GlobalOpacity > .01f; + renderableFlags.SetPickable(canModelBePickable + && (theModelContext.m_Model.m_Flags.IsGloballyPickable() + || renderableFlags.GetPickable())); + SRenderableObject *theRenderableObject(NULL); + eastl::pair<bool, SGraphObject *> theMaterialObjectAndDirty = + ResolveReferenceMaterial(theSourceMaterialObject); + SGraphObject *theMaterialObject = theMaterialObjectAndDirty.second; + subsetDirty = subsetDirty || theMaterialObjectAndDirty.first; + if (theMaterialObject == NULL) + continue; + + // set tessellation + if (inModel.m_TessellationMode != TessModeValues::NoTess) { + theSubset.m_PrimitiveType = NVRenderDrawMode::Patches; + // set tessellation factor + theSubset.m_EdgeTessFactor = inModel.m_EdgeTess; + theSubset.m_InnerTessFactor = inModel.m_InnerTess; + // update the vertex ver patch count in the input assembler + // currently we only support triangle patches so count is always 3 + theSubset.m_InputAssembler->SetPatchVertexCount(3); + theSubset.m_InputAssemblerDepth->SetPatchVertexCount(3); + // check wireframe mode + theSubset.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + subsetDirty = + subsetDirty | (theSubset.m_WireframeMode != inModel.m_WireframeMode); + inModel.m_WireframeMode = qt3dsContext.GetWireframeMode(); + } else { + theSubset.m_PrimitiveType = theSubset.m_InputAssembler->GetPrimitiveType(); + theSubset.m_InputAssembler->SetPatchVertexCount(1); + theSubset.m_InputAssemblerDepth->SetPatchVertexCount(1); + // currently we allow wirframe mode only if tessellation is on + theSubset.m_WireframeMode = false; + + subsetDirty = + subsetDirty | (theSubset.m_WireframeMode != inModel.m_WireframeMode); + inModel.m_WireframeMode = false; + } + // Only clear flags on the materials in this direct hierarchy. Do not clear them of + // this + // references materials in another hierarchy. + bool clearMaterialDirtyFlags = theMaterialObject == theSourceMaterialObject; + + if (theMaterialObject == NULL) + continue; + + if (theMaterialObject->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial &theMaterial( + static_cast<SDefaultMaterial &>(*theMaterialObject)); + SDefaultMaterialPreparationResult theMaterialPrepResult( + PrepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity, + clearMaterialDirtyFlags)); + SShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.m_MaterialKey; + subsetOpacity = theMaterialPrepResult.m_Opacity; + SRenderableImage *firstImage(theMaterialPrepResult.m_FirstImage); + subsetDirty |= theMaterialPrepResult.m_Dirty; + renderableFlags = theMaterialPrepResult.m_RenderableFlags; + + m_Renderer.DefaultMaterialShaderKeyProperties() + .m_TessellationMode.SetTessellationMode(theGeneratedKey, + inModel.m_TessellationMode, true); + + NVConstDataRef<QT3DSMat44> boneGlobals; + if (theSubset.m_Joints.size()) { + QT3DS_ASSERT(false); + } + + theRenderableObject = RENDER_FRAME_NEW(SSubsetRenderable)( + renderableFlags, theModelCenter, m_Renderer, theSubset, theMaterial, + theModelContext, subsetOpacity, firstImage, theGeneratedKey, boneGlobals); + subsetDirty = subsetDirty || renderableFlags.IsDirty(); + + } // if type == DefaultMaterial + else if (theMaterialObject->m_Type == GraphObjectTypes::CustomMaterial) { + SCustomMaterial &theMaterial( + static_cast<SCustomMaterial &>(*theMaterialObject)); + + ICustomMaterialSystem &theMaterialSystem( + qt3dsContext.GetCustomMaterialSystem()); + subsetDirty |= theMaterialSystem.PrepareForRender( + theModelContext.m_Model, theSubset, theMaterial, clearMaterialDirtyFlags); + + SDefaultMaterialPreparationResult theMaterialPrepResult( + PrepareCustomMaterialForRender(theMaterial, renderableFlags, + subsetOpacity)); + SShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.m_MaterialKey; + subsetOpacity = theMaterialPrepResult.m_Opacity; + SRenderableImage *firstImage(theMaterialPrepResult.m_FirstImage); + renderableFlags = theMaterialPrepResult.m_RenderableFlags; + + // prepare for render tells us if the object is transparent + if (theMaterial.m_hasTransparency) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + // prepare for render tells us if the object is transparent + if (theMaterial.m_hasRefraction) + renderableFlags |= RenderPreparationResultFlagValues::HasRefraction; + + m_Renderer.DefaultMaterialShaderKeyProperties() + .m_TessellationMode.SetTessellationMode(theGeneratedKey, + inModel.m_TessellationMode, true); + + if (theMaterial.m_IblProbe && CheckLightProbeDirty(*theMaterial.m_IblProbe)) { + m_Renderer.PrepareImageForIbl(*theMaterial.m_IblProbe); + } + + theRenderableObject = RENDER_FRAME_NEW(SCustomMaterialRenderable)( + renderableFlags, theModelCenter, m_Renderer, theSubset, theMaterial, + theModelContext, subsetOpacity, firstImage, theGeneratedKey); + } + if (theRenderableObject) { + theRenderableObject->m_ScopedLights = inScopedLights; + // set tessellation + theRenderableObject->m_TessellationMode = inModel.m_TessellationMode; + + if (theRenderableObject->m_RenderableFlags.HasTransparency() + || theRenderableObject->m_RenderableFlags.HasRefraction()) { + m_TransparentObjects.push_back(theRenderableObject); + } else { + m_OpaqueObjects.push_back(theRenderableObject); + } + } + } + } + return subsetDirty; + } + + bool SLayerRenderPreparationData::PrepareRenderablesForRender( + const QT3DSMat44 &inViewProjection, const Option<SClippingFrustum> &inClipFrustum, + QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags) + { + SStackPerfTimer __timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::PrepareRenderablesForRender"); + m_ViewProjection = inViewProjection; + QT3DSF32 theTextScaleFactor = inTextScaleFactor; + bool wasDataDirty = false; + bool hasTextRenderer = m_Renderer.GetQt3DSContext().GetTextRenderer() != NULL; + for (QT3DSU32 idx = 0, end = m_RenderableNodes.size(); idx < end; ++idx) { + SRenderableNodeEntry &theNodeEntry(m_RenderableNodes[idx]); + SNode *theNode = theNodeEntry.m_Node; + wasDataDirty = wasDataDirty || theNode->m_Flags.IsDirty(); + switch (theNode->m_Type) { + case GraphObjectTypes::Model: { + SModel *theModel = static_cast<SModel *>(theNode); + theModel->CalculateGlobalVariables(); + if (theModel->m_Flags.IsGloballyActive()) { + bool wasModelDirty = PrepareModelForRender( + *theModel, inViewProjection, inClipFrustum, theNodeEntry.m_Lights); + wasDataDirty = wasDataDirty || wasModelDirty; + } + } break; + case GraphObjectTypes::Text: { + if (hasTextRenderer) { + SText *theText = static_cast<SText *>(theNode); + theText->CalculateGlobalVariables(); + // Omit check for global active flag intentionally and force + // render preparation for all Text items. This eliminates + // large delay for distance field text items becoming active + // mid-animation. + bool wasTextDirty = PrepareTextForRender(*theText, inViewProjection, + theTextScaleFactor, ioFlags); + wasDataDirty = wasDataDirty || wasTextDirty; + + } + } break; + case GraphObjectTypes::Path: { + SPath *thePath = static_cast<SPath *>(theNode); + thePath->CalculateGlobalVariables(); + if (thePath->m_Flags.IsGloballyActive()) { + bool wasPathDirty = + PreparePathForRender(*thePath, inViewProjection, inClipFrustum, ioFlags); + wasDataDirty = wasDataDirty || wasPathDirty; + } + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + return wasDataDirty; + } + + bool SLayerRenderPreparationData::CheckLightProbeDirty(SImage &inLightProbe) + { + IQt3DSRenderContext &theContext(m_Renderer.GetQt3DSContext()); + return inLightProbe.ClearDirty(theContext.GetBufferManager(), + theContext.GetOffscreenRenderManager(), + theContext.GetRenderPluginManager(), true); + } + + struct SLightNodeMarker + { + SLight *m_Light; + QT3DSU32 m_LightIndex; + QT3DSU32 m_FirstValidIndex; + QT3DSU32 m_JustPastLastValidIndex; + bool m_AddOrRemove; + SLightNodeMarker() + : m_Light(NULL) + , m_FirstValidIndex(0) + , m_JustPastLastValidIndex(0) + , m_AddOrRemove(false) + { + } + SLightNodeMarker(SLight &inLight, QT3DSU32 inLightIndex, SNode &inNode, bool aorm) + : m_Light(&inLight) + , m_LightIndex(inLightIndex) + , m_AddOrRemove(aorm) + { + if (inNode.m_Type == GraphObjectTypes::Layer) { + m_FirstValidIndex = 0; + m_JustPastLastValidIndex = QT3DS_MAX_U32; + } else { + m_FirstValidIndex = inNode.m_DFSIndex; + SNode *lastChild = NULL; + SNode *firstChild = inNode.m_FirstChild; + // find deepest last child + while (firstChild) { + for (SNode *childNode = firstChild; childNode; + childNode = childNode->m_NextSibling) + lastChild = childNode; + + if (lastChild) + firstChild = lastChild->m_FirstChild; + else + firstChild = NULL; + } + if (lastChild) + // last valid index would be the last child index + 1 + m_JustPastLastValidIndex = lastChild->m_DFSIndex + 1; + else // no children. + m_JustPastLastValidIndex = m_FirstValidIndex + 1; + } + } + }; + + // m_Layer.m_Camera->CalculateViewProjectionMatrix(m_ViewProjection); + void + SLayerRenderPreparationData::PrepareForRender(const QSize &inViewportDimensions) + { + SStackPerfTimer __timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::PrepareForRender"); + if (m_LayerPrepResult.hasValue()) + return; + + m_Features.clear(); + m_FeatureSetHash = 0; + QT3DSVec2 thePresentationDimensions((QT3DSF32)inViewportDimensions.width(), + (QT3DSF32)inViewportDimensions.height()); + IRenderList &theGraph(m_Renderer.GetQt3DSContext().GetRenderList()); + NVRenderRect theViewport(theGraph.GetViewport()); + NVRenderRect theScissor(theGraph.GetViewport()); + if (theGraph.IsScissorTestEnabled()) + theScissor = m_Renderer.GetContext().GetScissorRect(); + bool wasDirty = false; + bool wasDataDirty = false; + wasDirty = m_Layer.m_Flags.IsDirty(); + // The first pass is just to render the data. + QT3DSU32 maxNumAAPasses = m_Layer.m_ProgressiveAAMode == AAModeValues::NoAA + ? (QT3DSU32)0 + : (QT3DSU32)(m_Layer.m_ProgressiveAAMode) + 1; + maxNumAAPasses = NVMin((QT3DSU32)(MAX_AA_LEVELS + 1), maxNumAAPasses); + SEffect *theLastEffect = NULL; + // Uncomment the line below to disable all progressive AA. + // maxNumAAPasses = 0; + + SLayerRenderPreparationResult thePrepResult; + bool hasOffscreenRenderer = GetOffscreenRenderer(); + + bool SSAOEnabled = (m_Layer.m_AoStrength > 0.0f && m_Layer.m_AoDistance > 0.0f); + bool SSDOEnabled = (m_Layer.m_ShadowStrength > 0.0f && m_Layer.m_ShadowDist > 0.0f); + SetShaderFeature("QT3DS_ENABLE_SSAO", SSAOEnabled); + SetShaderFeature("QT3DS_ENABLE_SSDO", SSDOEnabled); + bool requiresDepthPrepass = (hasOffscreenRenderer == false) && (SSAOEnabled || SSDOEnabled); + SetShaderFeature("QT3DS_ENABLE_SSM", false); // by default no shadow map generation + + if (m_Layer.m_Flags.IsActive()) { + // Get the layer's width and height. + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + for (SEffect *theEffect = m_Layer.m_FirstEffect; theEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsDirty()) { + wasDirty = true; + theEffect->m_Flags.SetDirty(false); + } + if (theEffect->m_Flags.IsActive()) { + // If the effect uses subpresentations, those have to be rendered before + // the effect itself + theEffectSystem.renderSubpresentations(*theEffect); + theLastEffect = theEffect; + if (hasOffscreenRenderer == false + && theEffectSystem.DoesEffectRequireDepthTexture(theEffect->m_ClassName)) + requiresDepthPrepass = true; + } + } + if (m_Layer.m_Flags.IsDirty()) { + wasDirty = true; + m_Layer.CalculateGlobalVariables(); + } + + bool shouldRenderToTexture = true; + + if (hasOffscreenRenderer) { + // We don't render to texture with offscreen renderers, we just render them to the + // viewport. + shouldRenderToTexture = false; + // Progaa disabled when using offscreen rendering. + maxNumAAPasses = 0; + } + + thePrepResult = SLayerRenderPreparationResult(SLayerRenderHelper( + theViewport, theScissor, m_Layer.m_Scene->m_Presentation->m_PresentationDimensions, + m_Layer, shouldRenderToTexture, m_Renderer.GetQt3DSContext().GetScaleMode(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor())); + thePrepResult.m_LastEffect = theLastEffect; + thePrepResult.m_MaxAAPassIndex = maxNumAAPasses; + thePrepResult.m_Flags.SetRequiresDepthTexture(requiresDepthPrepass + || NeedsWidgetTexture()); + thePrepResult.m_Flags.SetShouldRenderToTexture(shouldRenderToTexture); + if (m_Renderer.GetContext().GetRenderContextType() != NVRenderContextValues::GLES2) + thePrepResult.m_Flags.SetRequiresSsaoPass(SSAOEnabled); + + if (thePrepResult.IsLayerVisible()) { + if (shouldRenderToTexture) { + m_Renderer.GetQt3DSContext().GetRenderList().AddRenderTask( + CreateRenderToTextureRunnable()); + } + if (m_Layer.m_LightProbe && CheckLightProbeDirty(*m_Layer.m_LightProbe)) { + m_Renderer.PrepareImageForIbl(*m_Layer.m_LightProbe); + wasDataDirty = true; + } + + bool lightProbeValid = HasValidLightProbe(m_Layer.m_LightProbe); + + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", lightProbeValid); + SetShaderFeature("QT3DS_ENABLE_IBL_FOV", m_Layer.m_ProbeFov < 180.0f); + + if (lightProbeValid && m_Layer.m_LightProbe2 + && CheckLightProbeDirty(*m_Layer.m_LightProbe2)) { + m_Renderer.PrepareImageForIbl(*m_Layer.m_LightProbe2); + wasDataDirty = true; + } + + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE_2", + lightProbeValid && HasValidLightProbe(m_Layer.m_LightProbe2)); + + // Push nodes in reverse depth first order + if (m_RenderableNodes.empty()) { + m_CamerasAndLights.clear(); + QT3DSU32 dfsIndex = 0; + for (SNode *theChild = m_Layer.m_FirstChild; theChild; + theChild = theChild->m_NextSibling) + MaybeQueueNodeForRender(*theChild, m_RenderableNodes, m_CamerasAndLights, + dfsIndex); + reverse(m_CamerasAndLights.begin(), m_CamerasAndLights.end()); + reverse(m_RenderableNodes.begin(), m_RenderableNodes.end()); + m_LightToNodeMap.clear(); + } + m_Camera = NULL; + m_Lights.clear(); + m_OpaqueObjects.clear(); + m_TransparentObjects.clear(); + nvvector<SLightNodeMarker> theLightNodeMarkers(m_Renderer.GetPerFrameAllocator(), + "LightNodeMarkers"); + m_SourceLightDirections.clear(); + + for (QT3DSU32 idx = 0, end = m_CamerasAndLights.size(); idx < end; ++idx) { + SNode *theNode(m_CamerasAndLights[idx]); + wasDataDirty = wasDataDirty || theNode->m_Flags.IsDirty(); + switch (theNode->m_Type) { + case GraphObjectTypes::Camera: { + SCamera *theCamera = static_cast<SCamera *>(theNode); + if (theCamera->m_Flags.IsActive()) { + // Only proceed with camera nodes which are currently active. + // SetupCameraForRender() sets the camera used for picking and + // updates global state e.g. IsGloballyActive() + SCameraGlobalCalculationResult theResult = + thePrepResult.SetupCameraForRender(*theCamera); + wasDataDirty = wasDataDirty || theResult.m_WasDirty; + if (theCamera->m_Flags.IsGloballyActive()) + m_Camera = theCamera; + if (theResult.m_ComputeFrustumSucceeded == false) { + qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum"); + } + } + } break; + case GraphObjectTypes::Light: { + SLight *theLight = static_cast<SLight *>(theNode); + bool lightResult = theLight->CalculateGlobalVariables(); + wasDataDirty = lightResult || wasDataDirty; + // Note we setup the light index such that it is completely invariant of if + // the + // light is active or scoped. + QT3DSU32 lightIndex = (QT3DSU32)m_SourceLightDirections.size(); + m_SourceLightDirections.push_back(QT3DSVec3(0.0f)); + // Note we still need a light check when building the renderable light list. + // We also cannot cache shader-light bindings based on layers any more + // because + // the number of lights for a given renderable does not depend on the layer + // as it used to but + // additional perhaps on the light's scoping rules. + if (theLight->m_Flags.IsGloballyActive()) { + if (theLight->m_Scope == NULL) { + m_Lights.push_back(theLight); + if (m_Renderer.GetContext().GetRenderContextType() + != NVRenderContextValues::GLES2 + && theLight->m_CastShadow && GetShadowMapManager()) { + // PKC -- use of "res" as an exponent of two is an annoying + // artifact of the XML interface + // I'll change this with an enum interface later on, but that's + // less important right now. + QT3DSU32 mapSize = 1 << theLight->m_ShadowMapRes; + ShadowMapModes::Enum mapMode = + (theLight->m_LightType != RenderLightTypes::Directional) + ? ShadowMapModes::CUBE + : ShadowMapModes::VSM; + m_ShadowMapManager->AddShadowMapEntry( + m_Lights.size() - 1, mapSize, mapSize, + NVRenderTextureFormats::R16F, 1, mapMode, + ShadowFilterValues::NONE); + thePrepResult.m_Flags.SetRequiresShadowMapPass(true); + SetShaderFeature("QT3DS_ENABLE_SSM", true); + } + } + TLightToNodeMap::iterator iter = + m_LightToNodeMap.insert(eastl::make_pair(theLight, (SNode *)NULL)) + .first; + SNode *oldLightScope = iter->second; + SNode *newLightScope = theLight->m_Scope; + + if (oldLightScope != newLightScope) { + iter->second = newLightScope; + if (oldLightScope) + theLightNodeMarkers.push_back(SLightNodeMarker( + *theLight, lightIndex, *oldLightScope, false)); + if (newLightScope) + theLightNodeMarkers.push_back(SLightNodeMarker( + *theLight, lightIndex, *newLightScope, true)); + } + if (newLightScope) { + m_SourceLightDirections.back() = + theLight->GetScalingCorrectDirection(); + } + } + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + + if (theLightNodeMarkers.empty() == false) { + for (QT3DSU32 idx = 0, end = m_RenderableNodes.size(); idx < end; ++idx) { + SRenderableNodeEntry &theNodeEntry(m_RenderableNodes[idx]); + QT3DSU32 nodeDFSIndex = theNodeEntry.m_Node->m_DFSIndex; + for (QT3DSU32 markerIdx = 0, markerEnd = theLightNodeMarkers.size(); + markerIdx < markerEnd; ++markerIdx) { + SLightNodeMarker &theMarker = theLightNodeMarkers[markerIdx]; + if (nodeDFSIndex >= theMarker.m_FirstValidIndex + && nodeDFSIndex < theMarker.m_JustPastLastValidIndex) { + if (theMarker.m_AddOrRemove) { + SNodeLightEntry *theNewEntry = + m_RenderableNodeLightEntryPool.construct( + theMarker.m_Light, theMarker.m_LightIndex, __FILE__, + __LINE__); + theNodeEntry.m_Lights.push_back(*theNewEntry); + } else { + for (TNodeLightEntryList::iterator + lightIter = theNodeEntry.m_Lights.begin(), + lightEnd = theNodeEntry.m_Lights.end(); + lightIter != lightEnd; ++lightIter) { + if (lightIter->m_Light == theMarker.m_Light) { + SNodeLightEntry &theEntry = *lightIter; + theNodeEntry.m_Lights.remove(theEntry); + m_RenderableNodeLightEntryPool.deallocate(&theEntry); + break; + } + } + } + } + } + } + } + + QT3DSF32 theTextScaleFactor = 1.0f; + if (m_Camera) { + m_Camera->CalculateViewProjectionMatrix(m_ViewProjection); + theTextScaleFactor = m_Camera->GetTextScaleFactor( + thePrepResult.GetLayerToPresentationViewport(), + thePrepResult.GetPresentationDesignDimensions()); + SClipPlane nearPlane; + QT3DSMat33 theUpper33(m_Camera->m_GlobalTransform.getUpper3x3InverseTranspose()); + + QT3DSVec3 dir(theUpper33.transform(QT3DSVec3(0, 0, -1))); + dir.normalize(); + nearPlane.normal = dir; + QT3DSVec3 theGlobalPos = m_Camera->GetGlobalPos() + m_Camera->m_ClipNear * dir; + nearPlane.d = -(dir.dot(theGlobalPos)); + // the near plane's bbox edges are calculated in the clipping frustum's + // constructor. + m_ClippingFrustum = SClippingFrustum(m_ViewProjection, nearPlane); + } else { + m_ViewProjection = QT3DSMat44::createIdentity(); + } + + // Setup the light directions here. + + for (QT3DSU32 lightIdx = 0, lightEnd = m_Lights.size(); lightIdx < lightEnd; + ++lightIdx) { + m_LightDirections.push_back(m_Lights[lightIdx]->GetScalingCorrectDirection()); + } + + m_ModelContexts.clear(); + if (GetOffscreenRenderer() == false) { + bool renderablesDirty = + PrepareRenderablesForRender(m_ViewProjection, + m_ClippingFrustum, + theTextScaleFactor, thePrepResult.m_Flags); + wasDataDirty = wasDataDirty || renderablesDirty; + if (thePrepResult.m_Flags.RequiresStencilBuffer()) + thePrepResult.m_Flags.SetShouldRenderToTexture(true); + } else { + NVRenderRect theViewport = + thePrepResult.GetLayerToPresentationViewport().ToIntegerRect(); + bool theScissor = true; + NVRenderRect theScissorRect = + thePrepResult.GetLayerToPresentationScissorRect().ToIntegerRect(); + // This happens here because if there are any fancy render steps + IRenderList &theRenderList(m_Renderer.GetQt3DSContext().GetRenderList()); + NVRenderContext &theContext(m_Renderer.GetContext()); + SRenderListScopedProperty<bool> _listScissorEnabled( + theRenderList, &IRenderList::IsScissorTestEnabled, + &IRenderList::SetScissorTestEnabled, theScissor); + SRenderListScopedProperty<NVRenderRect> _listViewport( + theRenderList, &IRenderList::GetViewport, &IRenderList::SetViewport, + theViewport); + SRenderListScopedProperty<NVRenderRect> _listScissor( + theRenderList, &IRenderList::GetScissor, &IRenderList::SetScissorRect, + theScissorRect); + // Some plugins don't use the render list so they need the actual gl context + // setup. + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, true); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __scissorRect( + theContext, &NVRenderContext::GetScissorRect, + &NVRenderContext::SetScissorRect, theScissorRect); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + theViewport); + SOffscreenRenderFlags theResult = m_LastFrameOffscreenRenderer->NeedsRender( + CreateOffscreenRenderEnvironment(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), &m_Layer); + wasDataDirty = wasDataDirty || theResult.m_HasChangedSinceLastFrame; + } + } + } + wasDirty = wasDirty || wasDataDirty; + thePrepResult.m_Flags.SetWasDirty(wasDirty); + thePrepResult.m_Flags.SetLayerDataDirty(wasDataDirty); + + m_LayerPrepResult = thePrepResult; + + // Per-frame cache of renderable objects post-sort. + GetOpaqueRenderableObjects(); + // If layer depth test is false, this may also contain opaque objects. + GetTransparentRenderableObjects(); + + GetCameraDirection(); + } + + void SLayerRenderPreparationData::ResetForFrame() + { + m_TransparentObjects.clear_unsafe(); + m_OpaqueObjects.clear_unsafe(); + m_LayerPrepResult.setEmpty(); + // The check for if the camera is or is not null is used + // to figure out if this layer was rendered at all. + m_Camera = NULL; + m_LastFrameOffscreenRenderer = NULL; + m_IRenderWidgets.clear_unsafe(); + m_CameraDirection.setEmpty(); + m_LightDirections.clear_unsafe(); + m_RenderedOpaqueObjects.clear_unsafe(); + m_RenderedTransparentObjects.clear_unsafe(); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h new file mode 100644 index 0000000..5b8d6e1 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_LAYER_RENDER_PREPARATION_DATA_H +#define QT3DS_RENDERER_IMPL_LAYER_RENDER_PREPARATION_DATA_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderableObjects.h" +#include "Qt3DSRenderClippingFrustum.h" +#include "Qt3DSRenderResourceTexture2D.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderProfiler.h" +#include "Qt3DSRenderShadowMap.h" +#include "foundation/Qt3DSPool.h" +#include "Qt3DSRenderableObjects.h" + +namespace qt3ds { +namespace render { + struct SLayerRenderData; + class Qt3DSRendererImpl; + struct SRenderableObject; + + struct LayerRenderPreparationResultFlagValues + { + enum Enum { + // Was the data in this layer dirty (meaning re-render to texture, possibly) + WasLayerDataDirty = 1, + // Was the data in this layer dirty *or* this layer *or* any effect dirty. + WasDirty = 1 << 1, + // An effect or flag or rotation on the layer dictates this object should + // render to the texture. + ShouldRenderToTexture = 1 << 2, + // Some effects require depth texturing, this should be set on the effect + // instance. + RequiresDepthTexture = 1 << 3, + + // Should create independent viewport + // If we aren't rendering to texture we still may have width/height manipulations + // that require our own viewport. + ShouldCreateIndependentViewport = 1 << 4, + + // SSAO should be done in a separate pass + // Note that having an AO pass necessitates a DepthTexture so this flag should + // never be set without the RequiresDepthTexture flag as well. + RequiresSsaoPass = 1 << 5, + + // if some light cause shadow + // we need a separate per light shadow map pass + RequiresShadowMapPass = 1 << 6, + + // Currently we use a stencil-cover algorithm to render bezier curves. + RequiresStencilBuffer = 1 << 7 + }; + }; + + struct SLayerRenderPreparationResultFlags + : public NVFlags<LayerRenderPreparationResultFlagValues::Enum, QT3DSU32> + { + bool WasLayerDataDirty() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::WasLayerDataDirty); + } + void SetLayerDataDirty(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::WasLayerDataDirty); + } + + bool WasDirty() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::WasDirty); + } + void SetWasDirty(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::WasDirty); + } + + bool ShouldRenderToTexture() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::ShouldRenderToTexture); + } + void SetShouldRenderToTexture(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::ShouldRenderToTexture); + } + + bool RequiresDepthTexture() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresDepthTexture); + } + void SetRequiresDepthTexture(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresDepthTexture); + } + + bool ShouldCreateIndependentViewport() const + { + return this->operator&( + LayerRenderPreparationResultFlagValues::ShouldCreateIndependentViewport); + } + void SetShouldCreateIndependentViewport(bool inValue) + { + clearOrSet(inValue, + LayerRenderPreparationResultFlagValues::ShouldCreateIndependentViewport); + } + + bool RequiresSsaoPass() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresSsaoPass); + } + void SetRequiresSsaoPass(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresSsaoPass); + } + + bool RequiresShadowMapPass() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresShadowMapPass); + } + void SetRequiresShadowMapPass(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresShadowMapPass); + } + + bool RequiresStencilBuffer() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresStencilBuffer); + } + void SetRequiresStencilBuffer(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresStencilBuffer); + } + }; + + struct SLayerRenderPreparationResult : public SLayerRenderHelper + { + SEffect *m_LastEffect; + SLayerRenderPreparationResultFlags m_Flags; + QT3DSU32 m_MaxAAPassIndex; + SLayerRenderPreparationResult() + : m_LastEffect(NULL) + , m_MaxAAPassIndex(0) + { + } + SLayerRenderPreparationResult(const SLayerRenderHelper &inHelper) + : SLayerRenderHelper(inHelper) + , m_LastEffect(NULL) + , m_MaxAAPassIndex(0) + { + } + }; + + struct SRenderableNodeEntry + { + SNode *m_Node; + TNodeLightEntryList m_Lights; + SRenderableNodeEntry() + : m_Node(NULL) + { + } + SRenderableNodeEntry(SNode &inNode) + : m_Node(&inNode) + { + } + }; + + struct SScopedLightsListScope + { + nvvector<SLight *> &m_LightsList; + nvvector<QT3DSVec3> &m_LightDirList; + QT3DSU32 m_ListOriginalSize; + SScopedLightsListScope(nvvector<SLight *> &inLights, nvvector<QT3DSVec3> &inDestLightDirList, + nvvector<QT3DSVec3> &inSrcLightDirList, + TNodeLightEntryList &inScopedLights) + : m_LightsList(inLights) + , m_LightDirList(inDestLightDirList) + , m_ListOriginalSize(m_LightsList.size()) + { + for (TNodeLightEntryList::iterator iter = inScopedLights.begin(), + end = inScopedLights.end(); + iter != end; ++iter) { + m_LightsList.push_back(iter->m_Light); + m_LightDirList.push_back(inSrcLightDirList[iter->m_LightIndex]); + } + } + ~SScopedLightsListScope() + { + m_LightsList.resize(m_ListOriginalSize); + m_LightDirList.resize(m_ListOriginalSize); + } + }; + + struct SDefaultMaterialPreparationResult + { + SRenderableImage *m_FirstImage; + QT3DSF32 m_Opacity; + SRenderableObjectFlags m_RenderableFlags; + SShaderDefaultMaterialKey m_MaterialKey; + bool m_Dirty; + + SDefaultMaterialPreparationResult(SShaderDefaultMaterialKey inMaterialKey); + }; + + // Data used strictly in the render preparation step. + struct SLayerRenderPreparationData + { + typedef void (*TRenderRenderableFunction)(SLayerRenderData &inData, + SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, + TShaderFeatureSet inShaderFeatures, + QT3DSU32 lightIndex, const SCamera &inCamera); + typedef nvhash_map<SLight *, SNode *> TLightToNodeMap; + typedef Pool<SNodeLightEntry, ForwardingAllocator> TNodeLightEntryPoolType; + + enum Enum { + MAX_AA_LEVELS = 8, + MAX_TEMPORAL_AA_LEVELS = 2, + }; + + SLayer &m_Layer; + Qt3DSRendererImpl &m_Renderer; + NVAllocatorCallback &m_Allocator; + // List of nodes we can render, not all may be active. Found by doing a depth-first + // search through m_FirstChild if length is zero. + + TNodeLightEntryPoolType m_RenderableNodeLightEntryPool; + nvvector<SRenderableNodeEntry> m_RenderableNodes; + TLightToNodeMap m_LightToNodeMap; // map of lights to nodes to cache if we have looked up a + // given scoped light yet. + // Built at the same time as the renderable nodes map. + // these are processed so they are available when the shaders for the models + // are being generated. + nvvector<SNode *> m_CamerasAndLights; + + // Results of prepare for render. + SCamera *m_Camera; + nvvector<SLight *> m_Lights; // Only contains lights that are global. + TRenderableObjectList m_OpaqueObjects; + TRenderableObjectList m_TransparentObjects; + // Sorted lists of the rendered objects. There may be other transforms applied so + // it is simplest to duplicate the lists. + TRenderableObjectList m_RenderedOpaqueObjects; + TRenderableObjectList m_RenderedTransparentObjects; + QT3DSMat44 m_ViewProjection; + SClippingFrustum m_ClippingFrustum; + Option<SLayerRenderPreparationResult> m_LayerPrepResult; + // Widgets drawn at particular times during the rendering process + nvvector<IRenderWidget *> m_IRenderWidgets; + Option<QT3DSVec3> m_CameraDirection; + // Scoped lights need a level of indirection into a light direction list. The source light + // directions list is as long as there are lights on the layer. It holds invalid + // information for + // any lights that are not both active and scoped; but the relative position for a given + // light + // in this list is completely constant and immutable; this relative position is saved on a + // structure + // and used when looking up the light direction for a given light. + nvvector<QT3DSVec3> m_SourceLightDirections; + nvvector<QT3DSVec3> m_LightDirections; + TModelContextPtrList m_ModelContexts; + NVScopedRefCounted<IOffscreenRenderer> m_LastFrameOffscreenRenderer; + + eastl::vector<SShaderPreprocessorFeature> m_Features; + CRegisteredString m_CGLightingFeatureName; + bool m_FeaturesDirty; + size_t m_FeatureSetHash; + bool m_TooManyLightsError; + + // shadow mapps + NVScopedRefCounted<Qt3DSShadowMap> m_ShadowMapManager; + + SLayerRenderPreparationData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer); + virtual ~SLayerRenderPreparationData(); + bool GetOffscreenRenderer(); + bool GetShadowMapManager(); + bool NeedsWidgetTexture() const; + + SShaderDefaultMaterialKey GenerateLightingKey(DefaultMaterialLighting::Enum inLightingType); + + void PrepareImageForRender(SImage &inImage, ImageMapTypes::Enum inMapType, + SRenderableImage *&ioFirstImage, SRenderableImage *&ioNextImage, + SRenderableObjectFlags &ioFlags, + SShaderDefaultMaterialKey &ioGeneratedShaderKey, + QT3DSU32 inImageIndex); + + SDefaultMaterialPreparationResult + PrepareDefaultMaterialForRender(SDefaultMaterial &inMaterial, + SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity, + bool inClearMaterialFlags); + + SDefaultMaterialPreparationResult + PrepareCustomMaterialForRender(SCustomMaterial &inMaterial, + SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity); + + bool PrepareModelForRender(SModel &inModel, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, + TNodeLightEntryList &inScopedLights); + + bool PrepareTextForRender(SText &inText, 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, + const Option<SClippingFrustum> &inClipFrustum, + QT3DSF32 inTextScaleFactor, + SLayerRenderPreparationResultFlags &ioFlags); + + // returns true if this object will render something different than it rendered the last + // time. + virtual void PrepareForRender(const QSize &inViewportDimensions); + bool CheckLightProbeDirty(SImage &inLightProbe); + void AddRenderWidget(IRenderWidget &inWidget); + void SetShaderFeature(const char *inName, bool inValue); + void SetShaderFeature(CRegisteredString inName, bool inValue); + NVConstDataRef<SShaderPreprocessorFeature> GetShaderFeatureSet(); + size_t GetShaderFeatureSetHash(); + // The graph object is not const because this traversal updates dirty state on the objects. + eastl::pair<bool, SGraphObject *> ResolveReferenceMaterial(SGraphObject *inMaterial); + + QT3DSVec3 GetCameraDirection(); + // Per-frame cache of renderable objects post-sort. + NVDataRef<SRenderableObject *> GetOpaqueRenderableObjects(); + // If layer depth test is false, this may also contain opaque objects. + NVDataRef<SRenderableObject *> GetTransparentRenderableObjects(); + + virtual void ResetForFrame(); + + // The render list and gl context are setup for what the embedded item will + // need. + virtual SOffscreenRendererEnvironment CreateOffscreenRenderEnvironment() = 0; + + virtual IRenderTask &CreateRenderToTextureRunnable() = 0; + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp new file mode 100644 index 0000000..a3d3fab --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp @@ -0,0 +1,3007 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRendererImpl.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "Qt3DSVertexPipelineImpl.h" + +// This adds support for the depth buffers in the shader so we can do depth +// texture-based effects. +#define QT3DS_RENDER_SUPPORT_DEPTH_TEXTURE 1 + +namespace qt3ds { +namespace render { + + void STextShader::Render(NVRenderTexture2D &inTexture, + const STextScaleAndOffset &inScaleAndOffset, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inMVP, const QT3DSVec2 &inCameraVec, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor) + { + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(&m_Shader); + m_MVP.Set(inMVP); + m_Sampler.Set(&inTexture); + m_TextColor.Set(inTextColor); + m_Dimensions.Set(QT3DSVec4(inScaleAndOffset.m_TextScale.x, inScaleAndOffset.m_TextScale.y, + inScaleAndOffset.m_TextOffset.x, inScaleAndOffset.m_TextOffset.y)); + m_CameraProperties.Set(inCameraVec); + STextureDetails theTextureDetails = inTexture.GetTextureDetails(); + QT3DSF32 theWidthScale = + (QT3DSF32)inTextTextureDetails.m_TextWidth / (QT3DSF32)theTextureDetails.m_Width; + QT3DSF32 theHeightScale = + (QT3DSF32)inTextTextureDetails.m_TextHeight / (QT3DSF32)theTextureDetails.m_Height; + m_BackgroundColor.Set(inBackgroundColor); + + m_TextDimensions.Set( + QT3DSVec3(theWidthScale, theHeightScale, inTextTextureDetails.m_FlipY ? 1.0f : 0.0f)); + inRenderContext.SetInputAssembler(&inInputAssemblerBuffer); + inRenderContext.Draw(NVRenderDrawMode::Triangles, count, 0); + } + + void STextShader::RenderPath(NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inPathFontSpec, + const STextScaleAndOffset &inScaleAndOffset, + const QT3DSVec4 &inTextColor, const QT3DSMat44 &inViewProjection, + const QT3DSMat44 &inModel, const QT3DSVec2 &, + NVRenderContext &inRenderContext, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor) + { + qt3ds::render::NVRenderBoolOp::Enum theDepthFunction = inRenderContext.GetDepthFunction(); + bool isDepthEnabled = inRenderContext.IsDepthTestEnabled(); + bool isStencilEnabled = inRenderContext.IsStencilTestEnabled(); + bool isDepthWriteEnabled = inRenderContext.IsDepthWriteEnabled(); + qt3ds::render::NVRenderStencilFunctionArgument theArg(qt3ds::render::NVRenderBoolOp::NotEqual, 0, + 0xFF); + qt3ds::render::NVRenderStencilOperationArgument theOpArg(qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Zero); + NVScopedRefCounted<NVRenderDepthStencilState> depthStencilState = + inRenderContext.CreateDepthStencilState(isDepthEnabled, isDepthWriteEnabled, + theDepthFunction, false, theArg, theArg, + theOpArg, theOpArg); + + inRenderContext.SetActiveShader(NULL); + inRenderContext.SetCullingEnabled(false); + + inRenderContext.SetDepthStencilState(depthStencilState); + + // setup transform + QT3DSMat44 offsetMatrix = QT3DSMat44::createIdentity(); + offsetMatrix.setPosition(QT3DSVec3( + inScaleAndOffset.m_TextOffset.x - (QT3DSF32)inTextTextureDetails.m_TextWidth / 2.0f, + inScaleAndOffset.m_TextOffset.y - (QT3DSF32)inTextTextureDetails.m_TextHeight / 2.0f, + 0.0)); + + QT3DSMat44 pathMatrix = inPathFontItem.GetTransform(); + + inRenderContext.SetPathProjectionMatrix(inViewProjection); + inRenderContext.SetPathModelViewMatrix(inModel * offsetMatrix * pathMatrix); + + // first pass + inPathFontSpec.StencilFillPathInstanced(inPathFontItem); + + // second pass + inRenderContext.SetActiveProgramPipeline(m_ProgramPipeline); + m_TextColor.Set(inTextColor); + m_BackgroundColor.Set(inBackgroundColor); + + inRenderContext.SetStencilTestEnabled(true); + inPathFontSpec.CoverFillPathInstanced(inPathFontItem); + + inRenderContext.SetStencilTestEnabled(isStencilEnabled); + inRenderContext.SetDepthFunction(theDepthFunction); + + inRenderContext.SetActiveProgramPipeline(NULL); + } + + void STextShader::Render2D(NVRenderTexture2D &inTexture, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inMVP, NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + QT3DSVec2 inVertexOffsets) + { + // inRenderContext.SetCullingEnabled( false ); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + + inRenderContext.SetActiveShader(&m_Shader); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + inRenderContext.SetBlendFunction(blendFunc); + inRenderContext.SetBlendEquation(blendEqu); + + m_MVP.Set(inMVP); + m_Sampler.Set(&inTexture); + m_TextColor.Set(inTextColor); + m_VertexOffsets.Set(inVertexOffsets); + + inRenderContext.SetInputAssembler(&inInputAssemblerBuffer); + inRenderContext.Draw(NVRenderDrawMode::Triangles, count, 0); + } + + using eastl::make_pair; + + static inline void AddVertexDepth(SShaderVertexCodeGenerator &vertexShader) + { + // near plane, far plane + vertexShader.AddInclude("viewProperties.glsllib"); + vertexShader.AddVarying("vertex_depth", "float"); + // the w coordinate is the unormalized distance to the object from the camera + // We want the normalized distance, with 0 representing the far plane and 1 representing + // the near plane, of the object in the vertex depth variable. + + vertexShader << "\tvertex_depth = calculateVertexDepth( camera_properties, gl_Position );" + << Endl; + } + + // Helper implements the vertex pipeline for mesh subsets when bound to the default material. + // Should be completely possible to use for custom materials with a bit of refactoring. + struct SSubsetMaterialVertexPipeline : public SVertexPipelineImpl + { + Qt3DSRendererImpl &m_Renderer; + SSubsetRenderable &m_Renderable; + TessModeValues::Enum m_TessMode; + + SSubsetMaterialVertexPipeline(Qt3DSRendererImpl &renderer, SSubsetRenderable &renderable, + bool inWireframeRequested) + : SVertexPipelineImpl(renderer.GetQt3DSContext().GetAllocator(), + renderer.GetQt3DSContext().GetDefaultMaterialShaderGenerator(), + renderer.GetQt3DSContext().GetShaderProgramGenerator(), + renderer.GetQt3DSContext().GetStringTable(), false) + , m_Renderer(renderer) + , m_Renderable(renderable) + , m_TessMode(TessModeValues::NoTess) + { + if (m_Renderer.GetContext().IsTessellationSupported()) { + m_TessMode = renderable.m_TessellationMode; + } + + if (m_Renderer.GetContext().IsGeometryStageSupported() + && m_TessMode != TessModeValues::NoTess) + m_Wireframe = inWireframeRequested; + } + + void InitializeTessControlShader() + { + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl) == NULL) + return; + + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + + SetupTessIncludes(ShaderGeneratorStages::TessControl, m_TessMode); + + tessCtrlShader.Append("void main() {\n"); + + tessCtrlShader.Append("\tctWorldPos[0] = varWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = varWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = varWorldPos[2];"); + + if (m_TessMode == TessModeValues::TessPhong + || m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctNorm[0] = varObjectNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = varObjectNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = varObjectNormal[2];"); + } + if (m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctTangent[0] = varTangent[0];"); + tessCtrlShader.Append("\tctTangent[1] = varTangent[1];"); + tessCtrlShader.Append("\tctTangent[2] = varTangent[2];"); + } + + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + } + void InitializeTessEvaluationShader() + { + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval) == NULL) + return; + + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + SetupTessIncludes(ShaderGeneratorStages::TessEval, m_TessMode); + + if (m_TessMode == TessModeValues::TessLinear) + m_Renderer.GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .AddDisplacementImageUniforms(tessEvalShader, m_DisplacementIdx, + m_DisplacementImage); + + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("normal_matrix", "mat3"); + + tessEvalShader.Append("void main() {"); + + if (m_TessMode == TessModeValues::TessNPatch) { + tessEvalShader.Append("\tctNorm[0] = varObjectNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = varObjectNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = varObjectNormalTC[2];"); + + tessEvalShader.Append("\tctTangent[0] = varTangentTC[0];"); + tessEvalShader.Append("\tctTangent[1] = varTangentTC[1];"); + tessEvalShader.Append("\tctTangent[2] = varTangentTC[2];"); + } + + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + } + + void FinalizeTessControlShader() + { + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessCtrlShader << "\t" << iter->first.c_str() + << "TC[gl_InvocationID] = " << iter->first.c_str() + << "[gl_InvocationID];\n"; + } + } + + void FinalizeTessEvaluationShader() + { + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + eastl::string outExt(""); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + outExt = "TE"; + + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + if (m_TessMode == TessModeValues::TessNPatch) { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.z * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.x * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[2];\n"; + } + + // transform the normal + if (m_GenerationFlags & GenerationFlagValues::WorldNormal) + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * teNorm);\n"; + // transform the tangent + if (m_GenerationFlags & GenerationFlagValues::TangentBinormal) { + tessEvalShader << "\n\tvarTangent" << outExt.c_str() + << " = normalize(normal_matrix * teTangent);\n"; + // transform the binormal + tessEvalShader << "\n\tvarBinormal" << outExt.c_str() + << " = normalize(normal_matrix * teBinormal);\n"; + } + } else { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.x * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.z * " << iter->first.c_str() << "TC[2];\n"; + } + + // displacement mapping makes only sense with linear tessellation + if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) { + IDefaultMaterialShaderGenerator::SImageVariableNames theNames = + m_Renderer.GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .GetImageVariableNames(m_DisplacementIdx); + tessEvalShader << "\tpos.xyz = defaultMaterialFileDisplacementTexture( " + << theNames.m_ImageSampler << ", displaceAmount, " + << theNames.m_ImageFragCoords << outExt.c_str(); + tessEvalShader << ", varObjectNormal" << outExt.c_str() << ", pos.xyz );" + << Endl; + tessEvalShader << "\tvarWorldPos" << outExt.c_str() + << "= (model_matrix * pos).xyz;" << Endl; + tessEvalShader << "\tvarViewVector" << outExt.c_str() + << "= normalize(camera_position - " + << "varWorldPos" << outExt.c_str() << ");" << Endl; + } + + // transform the normal + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * varObjectNormal" << outExt.c_str() + << ");\n"; + } + + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + if (m_TessMode != TessModeValues::NoTess) { + theStages |= ShaderGeneratorStages::TessControl; + theStages |= ShaderGeneratorStages::TessEval; + } + if (m_Wireframe) { + theStages |= ShaderGeneratorStages::Geometry; + } + ProgramGenerator().BeginProgram(theStages); + if (m_TessMode != TessModeValues::NoTess) { + InitializeTessControlShader(); + InitializeTessEvaluationShader(); + } + if (m_Wireframe) { + InitializeWireframeGeometryShader(); + } + // Open up each stage. + IShaderStageGenerator &vertexShader(Vertex()); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader << "void main()" << Endl << "{" << Endl; + vertexShader << "\tvec3 uTransform;" << Endl; + vertexShader << "\tvec3 vTransform;" << Endl; + + if (displacementImage) { + GenerateUVCoords(); + MaterialGenerator().GenerateImageUVCoordinates(*this, displacementImageIdx, 0, + *displacementImage); + if (!HasTessellation()) { + vertexShader.AddUniform("displaceAmount", "float"); + // we create the world position setup here + // because it will be replaced with the displaced position + SetCode(GenerationFlagValues::WorldPosition); + vertexShader.AddUniform("model_matrix", "mat4"); + + vertexShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + IDefaultMaterialShaderGenerator::SImageVariableNames theVarNames = + MaterialGenerator().GetImageVariableNames(displacementImageIdx); + + vertexShader.AddUniform(theVarNames.m_ImageSampler, "sampler2D"); + + vertexShader + << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + << theVarNames.m_ImageSampler << ", displaceAmount, " + << theVarNames.m_ImageFragCoords << ", attr_norm, attr_pos );" << Endl; + AddInterpolationParameter("varWorldPos", "vec3"); + vertexShader.Append("\tvec3 local_model_world_position = (model_matrix * " + "vec4(displacedPos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + } + // for tessellation we pass on the position in object coordinates + // Also note that gl_Position is written in the tess eval shader + if (HasTessellation()) + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + else { + vertexShader.AddUniform("model_view_projection", "mat4"); + if (displacementImage) + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + else + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + } + + if (HasTessellation()) { + GenerateWorldPosition(); + GenerateWorldNormal(); + GenerateObjectNormal(); + GenerateVarTangentAndBinormal(); + } + } + + void BeginFragmentGeneration() override + { + Fragment().AddUniform("material_diffuse", "vec4"); + Fragment() << "void main()" << Endl << "{" << Endl; + // We do not pass object opacity through the pipeline. + Fragment() << "\tfloat object_opacity = material_diffuse.a;" << Endl; + } + + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValue) override + { + Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n"; + } + void DoGenerateUVCoords(QT3DSU32 inUVSet = 0) override + { + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) { + Vertex().AddIncoming("attr_uv0", "vec2"); + Vertex() << "\tvarTexCoord0 = attr_uv0;" << Endl; + } else if (inUVSet == 1) { + Vertex().AddIncoming("attr_uv1", "vec2"); + Vertex() << "\tvarTexCoord1 = attr_uv1;" << Endl; + } + } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void DoGenerateWorldNormal() override + { + IShaderStageGenerator &vertexGenerator(Vertex()); + vertexGenerator.AddIncoming("attr_norm", "vec3"); + vertexGenerator.AddUniform("normal_matrix", "mat3"); + if (HasTessellation() == false) { + vertexGenerator.Append( + "\tvec3 world_normal = normalize(normal_matrix * attr_norm).xyz;"); + vertexGenerator.Append("\tvarNormal = world_normal;"); + } + } + void DoGenerateObjectNormal() override + { + AddInterpolationParameter("varObjectNormal", "vec3"); + Vertex().Append("\tvarObjectNormal = attr_norm;"); + } + void DoGenerateWorldPosition() override + { + Vertex().Append( + "\tvec3 local_model_world_position = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + + void DoGenerateVarTangentAndBinormal() override + { + Vertex().AddIncoming("attr_textan", "vec3"); + Vertex().AddIncoming("attr_binormal", "vec3"); + + bool hasNPatchTessellation = m_TessMode == TessModeValues::TessNPatch; + + if (!hasNPatchTessellation) { + Vertex() << "\tvarTangent = normal_matrix * attr_textan;" << Endl + << "\tvarBinormal = normal_matrix * attr_binormal;" << Endl; + } else { + Vertex() << "\tvarTangent = attr_textan;" << Endl + << "\tvarBinormal = attr_binormal;" << Endl; + } + } + + void DoGenerateVertexColor() override + { + Vertex().AddIncoming("attr_color", "vec3"); + Vertex().Append("\tvarColor = attr_color;"); + } + + void EndVertexGeneration(bool) override + { + + if (HasTessellation()) { + // finalize tess control shader + FinalizeTessControlShader(); + // finalize tess evaluation shader + FinalizeTessEvaluationShader(); + + TessControl().Append("}"); + TessEval().Append("}"); + } + if (m_Wireframe) { + // finalize geometry shader + FinalizeWireframeGeometryShader(); + Geometry().Append("}"); + } + Vertex().Append("}"); + } + + void EndFragmentGeneration(bool) override { Fragment().Append("}"); } + + void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override + { + m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType))); + Vertex().AddOutgoing(inName, inType); + Fragment().AddIncoming(inName, inType); + if (HasTessellation()) { + eastl::string nameBuilder(inName); + nameBuilder.append("TC"); + TessControl().AddOutgoing(nameBuilder.c_str(), inType); + + nameBuilder.assign(inName); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) { + nameBuilder.append("TE"); + Geometry().AddOutgoing(inName, inType); + } + TessEval().AddOutgoing(nameBuilder.c_str(), inType); + } + } + + IShaderStageGenerator &ActiveStage() override { return Vertex(); } + }; + + NVRenderShaderProgram *Qt3DSRendererImpl::GenerateShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet) + { + // build a string that allows us to print out the shader we are generating to the log. + // This is time consuming but I feel like it doesn't happen all that often and is very + // useful to users + // looking at the log file. + QLatin1String logPrefix("mesh subset pipeline-- "); + + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(logPrefix.data()); + + SShaderDefaultMaterialKey theKey(inRenderable.m_ShaderDescription); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr(m_GeneratedShaderString.c_str()); + NVRenderShaderProgram *cachedProgram = theCache.GetProgram(theCacheKey, inFeatureSet); + if (cachedProgram) + return cachedProgram; + + SSubsetMaterialVertexPipeline pipeline( + *this, inRenderable, + m_DefaultMaterialShaderKeyProperties.m_WireframeMode.GetValue(theKey)); + return m_qt3dsContext.GetDefaultMaterialShaderGenerator().GenerateShader( + inRenderable.m_Material, inRenderable.m_ShaderDescription, pipeline, inFeatureSet, + m_CurrentLayer->m_Lights, inRenderable.m_FirstImage, + inRenderable.m_RenderableFlags.HasTransparency(), + logPrefix.data()); + } + + // -------------- Special cases for shadows ------------------- + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetParaboloidDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetParaboloidDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetParaboloidDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetParaboloidDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetParaboloidDepthTessNPatchShader(); + } + + return GetParaboloidDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + IShaderProgramGenerator::OutputParaboloidDepthVertex(vertexShader); + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess linear shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess phong shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess NPatch shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetCubeShadowDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetCubeDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetCubeDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetCubeDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetCubeDepthTessNPatchShader(); + } + + return GetCubeDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + + IShaderProgramGenerator::OutputCubeFaceDepthVertex(vertexShader); + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth linear tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth phong tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth npatch tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tctNorm[0] = outNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = outNormalTC[2];"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetOrthographicDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetOrthographicDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetOrthographicDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetOrthographicDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetOrthographicDepthTessNPatchShader(); + } + + return GetOrthographicDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddOutgoing("outDepth", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append( + " gl_Position = model_view_projection * vec4( attr_pos, 1.0 );"); + vertexShader.Append(" outDepth.x = gl_Position.z / gl_Position.w;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess linear shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess phong shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess npatch shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + fragmentShader.AddUniform("model_view_projection", "mat4"); + fragmentShader.AddUniform("camera_properties", "vec2"); + fragmentShader.AddUniform("camera_position", "vec3"); + fragmentShader.AddUniform("camera_direction", "vec3"); + fragmentShader.AddInclude("depthpass.glsllib"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + // fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("\tfloat depth = (outDepth.x - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + // --------------------------------- + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthPrepassShader(bool inDisplaced) + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthPrePassShader = + (!inDisplaced) ? m_DepthPrepassShader : m_DepthPrepassShaderDisplaced; + + if (theDepthPrePassShader.hasValue() == false) { + // check if we do displacement mapping + TStrType name; + name.assign("depth prepass shader"); + if (inDisplaced) + name.append(" displacement"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + + if (inDisplaced) { + GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .AddDisplacementMappingForDepthPass(vertexShader); + } else { + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + } + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return theDepthPrePassShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetDepthTessPrepassShader(TessModeValues::Enum inTessMode, bool inDisplaced) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetDepthPrepassShader(inDisplaced); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetDepthTessLinearPrepassShader(inDisplaced); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetDepthTessPhongPrepassShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetDepthTessNPatchPrepassShader(); + } + + return GetDepthPrepassShader(inDisplaced); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetDepthTessLinearPrepassShader(bool inDisplaced) + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthPrePassShader = + (!inDisplaced) ? m_DepthTessLinearPrepassShader + : m_DepthTessLinearPrepassShaderDisplaced; + + if (theDepthPrePassShader.hasValue() == false) { + // check if we do displacement mapping + TStrType name; + name.assign("depth tess linear prepass shader"); + if (inDisplaced) + name.append(" displacement"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + if (inDisplaced) { + vertexShader.AddIncoming("attr_uv0", "vec2"); + vertexShader.AddIncoming("attr_norm", "vec3"); + + vertexShader.AddUniform("displacementMap_rot", "vec4"); + vertexShader.AddUniform("displacementMap_offset", "vec3"); + + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outUV", "vec2"); + } + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + if (inDisplaced) { + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, " + "displacementMap_rot.y, displacementMap_offset.x );"); + vertexShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, " + "displacementMap_rot.w, displacementMap_offset.y );"); + vertexShader.AddInclude( + "defaultMaterialLighting.glsllib"); // getTransformedUVCoords is in the + // lighting code addition. + vertexShader << "\tvec2 uv_coords = attr_uv0;" << Endl; + vertexShader << "\toutUV = getTransformedUVCoords( vec3( uv_coords, 1.0), " + "uTransform, vTransform );\n"; + } + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.AddOutgoing("outUVTC", "vec2"); + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + + if (inDisplaced) { + tessCtrlShader.Append("\toutUVTC[gl_InvocationID] = outUV[gl_InvocationID];"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];"); + } + + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + if (inDisplaced) { + tessEvalShader.AddUniform("displacementSampler", "sampler2D"); + tessEvalShader.AddUniform("displaceAmount", "float"); + tessEvalShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + } + tessEvalShader.AddOutgoing("outUV", "vec2"); + tessEvalShader.AddOutgoing("outNormal", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + + if (inDisplaced) { + tessEvalShader << "\toutUV = gl_TessCoord.x * outUVTC[0] + gl_TessCoord.y * " + "outUVTC[1] + gl_TessCoord.z * outUVTC[2];" + << Endl; + tessEvalShader + << "\toutNormal = gl_TessCoord.x * outNormalTC[0] + gl_TessCoord.y * " + "outNormalTC[1] + gl_TessCoord.z * outNormalTC[2];" + << Endl; + tessEvalShader + << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, outUV , outNormal, pos.xyz );" + << Endl; + tessEvalShader.Append( + "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } else + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return theDepthPrePassShader->mPtr; + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthTessPhongPrepassShader() + { + if (m_DepthTessPhongPrepassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth tess phong prepass shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth tess phong prepass shader", theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + m_DepthTessPhongPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + m_DepthTessPhongPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return m_DepthTessPhongPrepassShader->mPtr; + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthTessNPatchPrepassShader() + { + if (m_DepthTessNPatchPrepassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth tess npatch prepass shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tctTangent[0] = outNormal[0];"); // we don't care for the tangent + tessCtrlShader.Append("\tctTangent[1] = outNormal[1];"); + tessCtrlShader.Append("\tctTangent[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tctNorm[0] = outNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = outNormalTC[2];"); + tessEvalShader.Append( + "\tctTangent[0] = outNormalTC[0];"); // we don't care for the tangent + tessEvalShader.Append("\tctTangent[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctTangent[2] = outNormalTC[2];"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth tess npatch prepass shader", theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + m_DepthTessNPatchPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + m_DepthTessNPatchPrepassShader = + NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return m_DepthTessNPatchPrepassShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetDefaultAoPassShader(TShaderFeatureSet inFeatureSet) + { + if (m_DefaultAoPassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("fullscreen AO pass shader"); + NVRenderShaderProgram *aoPassShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!aoPassShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("uv_coords", "vec2"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append("\tuv_coords = attr_uv;"); + theVertexGenerator.Append("}"); + + // fragmentGenerator.AddInclude( "SSAOCustomMaterial.glsllib" ); + theFragmentGenerator.AddInclude("viewProperties.glsllib"); + theFragmentGenerator.AddInclude("screenSpaceAO.glsllib"); + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theFragmentGenerator + << "\tuniform vec4 ao_properties;" << Endl + << "\tuniform vec4 ao_properties2;" << Endl + << "\tuniform vec4 shadow_properties;" << Endl + << "\tuniform vec4 aoScreenConst;" << Endl + << "\tuniform vec4 UvToEyeConst;" << Endl; + } else { + theFragmentGenerator + << "layout (std140) uniform cbAoShadow { " << Endl << "\tvec4 ao_properties;" + << Endl << "\tvec4 ao_properties2;" << Endl << "\tvec4 shadow_properties;" + << Endl << "\tvec4 aoScreenConst;" << Endl << "\tvec4 UvToEyeConst;" << Endl + << "};" << Endl; + } + theFragmentGenerator.AddUniform("camera_direction", "vec3"); + theFragmentGenerator.AddUniform("depth_sampler", "sampler2D"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator << "\tfloat aoFactor;" << Endl; + theFragmentGenerator << "\tvec3 screenNorm;" << Endl; + + // We're taking multiple depth samples and getting the derivatives at each of them + // to get a more + // accurate view space normal vector. When we do only one, we tend to get bizarre + // values at the edges + // surrounding objects, and this also ends up giving us weird AO values. + // If we had a proper screen-space normal map, that would also do the trick. + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theFragmentGenerator.AddUniform("depth_sampler_size", "vec2"); + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append("\tfloat depth = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords)" + " / depth_sampler_size), camera_properties );"); + theFragmentGenerator.Append( + "\tdepth = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tdepth = (depth - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + theFragmentGenerator.Append("\tfloat depth2 = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords+ivec2(1))" + " / depth_sampler_size), camera_properties );"); + theFragmentGenerator.Append( + "\tdepth2 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tfloat depth3 = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords-ivec2(1))" + " / depth_sampler_size), camera_properties );"); + } else { + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append("\tfloat depth = getDepthValue( " + "texelFetch(depth_sampler, iCoords, 0), " + "camera_properties );"); + theFragmentGenerator.Append( + "\tdepth = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tdepth = (depth - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + theFragmentGenerator.Append("\tfloat depth2 = getDepthValue( " + "texelFetch(depth_sampler, iCoords+ivec2(1), 0), " + "camera_properties );"); + theFragmentGenerator.Append( + "\tdepth2 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tfloat depth3 = getDepthValue( " + "texelFetch(depth_sampler, iCoords-ivec2(1), 0), " + "camera_properties );"); + } + theFragmentGenerator.Append( + "\tdepth3 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tvec3 tanU = vec3(10, 0, dFdx(depth));"); + theFragmentGenerator.Append("\tvec3 tanV = vec3(0, 10, dFdy(depth));"); + theFragmentGenerator.Append("\tscreenNorm = normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\ttanU = vec3(10, 0, dFdx(depth2));"); + theFragmentGenerator.Append("\ttanV = vec3(0, 10, dFdy(depth2));"); + theFragmentGenerator.Append("\tscreenNorm += normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\ttanU = vec3(10, 0, dFdx(depth3));"); + theFragmentGenerator.Append("\ttanV = vec3(0, 10, dFdy(depth3));"); + theFragmentGenerator.Append("\tscreenNorm += normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\tscreenNorm = -normalize(screenNorm);"); + + theFragmentGenerator.Append("\taoFactor = \ + SSambientOcclusion( depth_sampler, screenNorm, ao_properties, ao_properties2, \ + camera_properties, aoScreenConst, UvToEyeConst );"); + + theFragmentGenerator.Append( + "\tgl_FragColor = vec4(aoFactor, aoFactor, aoFactor, 1.0);"); + + theFragmentGenerator.Append("}"); + } + + aoPassShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "fullscreen AO pass shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (aoPassShaderProgram) { + m_DefaultAoPassShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)(*aoPassShaderProgram, + GetContext())); + } else { + m_DefaultAoPassShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_DefaultAoPassShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetFakeDepthShader(TShaderFeatureSet inFeatureSet) + { + if (m_FakeDepthShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth display shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("uv_coords", "vec2"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append("\tuv_coords = attr_uv;"); + theVertexGenerator.Append("}"); + + theFragmentGenerator.AddUniform("depth_sampler", "sampler2D"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append( + "\tfloat depSample = texelFetch(depth_sampler, iCoords, 0).x;"); + theFragmentGenerator.Append( + "\tgl_FragColor = vec4( depSample, depSample, depSample, 1.0 );"); + theFragmentGenerator.Append("\treturn;"); + theFragmentGenerator.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth display shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (depthShaderProgram) { + m_FakeDepthShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)( + *depthShaderProgram, GetContext())); + } else { + m_FakeDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_FakeDepthShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetFakeCubeDepthShader(TShaderFeatureSet inFeatureSet) + { + if (!m_FakeCubemapDepthShader.hasValue()) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("cube depth display shader"); + NVRenderShaderProgram *cubeShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!cubeShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("sample_dir", "vec3"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append( + "\tsample_dir = vec3(4.0 * (attr_uv.x - 0.5), -1.0, 4.0 * (attr_uv.y - 0.5));"); + theVertexGenerator.Append("}"); + theFragmentGenerator.AddUniform("depth_cube", "samplerCube"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append( + "\tfloat smpDepth = texture( depth_cube, sample_dir ).x;"); + theFragmentGenerator.Append( + "\tgl_FragColor = vec4(smpDepth, smpDepth, smpDepth, 1.0);"); + theFragmentGenerator.Append("}"); + } + + cubeShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "cube depth display shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (cubeShaderProgram) { + m_FakeCubemapDepthShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)(*cubeShaderProgram, + GetContext())); + } else { + m_FakeCubemapDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_FakeCubemapDepthShader.getValue(); + } + + STextRenderHelper Qt3DSRendererImpl::GetTextShader(bool inUsePathRendering) + { + STextShaderPtr &thePtr = (!inUsePathRendering) ? m_TextShader : m_TextPathShader; + if (thePtr.HasGeneratedShader()) + return STextRenderHelper(thePtr, *m_QuadInputAssembler); + + NVRenderShaderProgram *theShader = NULL; + NVRenderProgramPipeline *thePipeline = NULL; + + if (!inUsePathRendering) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator + << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" << Endl; + fragmentGenerator.Append( + "\tvec4 c = texture2D(text_image, theCoords);"); + fragmentGenerator.Append( + "\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, c.rgb), c.a) * text_textcolor.a;"); + + vertexGenerator.Append("}"); + fragmentGenerator.Append("}"); + const char *shaderName = "text shader"; + theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet(), false); + } else { + GetProgramGenerator().BeginProgram( + TShaderGeneratorStageFlags(ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, text_textcolor.a), text_textcolor.a );"); + fragmentGenerator.Append("}"); + + const char *shaderName = "text path shader"; + theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet(), true); + + // setup program pipeline + if (theShader) { + thePipeline = GetContext().CreateProgramPipeline(); + if (thePipeline) { + thePipeline->SetProgramStages( + theShader, + qt3ds::render::NVRenderShaderTypeFlags(NVRenderShaderTypeValue::Fragment)); + } + } + } + + if (theShader == NULL) { + thePtr.Set(NULL); + } else { + GenerateXYQuad(); + thePtr.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader, thePipeline)); + } + return STextRenderHelper(thePtr, *m_QuadInputAssembler); + } + + STextDepthShader *Qt3DSRendererImpl::GetTextDepthShader() + { + if (m_TextDepthPrepassShader.hasValue() == false) { + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator + << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" << Endl; + fragmentGenerator.Append("\tfloat alpha_mask = texture2D( text_image, theCoords ).r;"); + fragmentGenerator.Append("\tif ( alpha_mask < .05 ) discard;"); + vertexGenerator.Append("}"); + fragmentGenerator.Append("}"); + const char *shaderName = "text depth shader"; + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet()); + if (theShader == NULL) { + m_TextDepthPrepassShader = NVScopedRefCounted<STextDepthShader>(); + } else { + GenerateXYQuad(); + m_TextDepthPrepassShader = NVScopedRefCounted<STextDepthShader>( + QT3DS_NEW(m_Context->GetAllocator(), STextDepthShader)( + m_Context->GetAllocator(), *theShader, *m_QuadInputAssembler)); + } + } + return m_TextDepthPrepassShader->mPtr; + } + + STextRenderHelper Qt3DSRendererImpl::GetTextWidgetShader() + { + if (m_TextWidgetShader.HasGeneratedShader()) + return STextRenderHelper(m_TextWidgetShader, *m_QuadInputAssembler); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" + << Endl; + fragmentGenerator.Append( + "\tfloat alpha_mask = texture2D( text_image, theCoords ).r * text_textcolor.a;"); + fragmentGenerator.Append("\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, alpha_mask), 1.0 );"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "text widget shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (theShader == NULL) + m_TextWidgetShader.Set(NULL); + else { + GenerateXYQuad(); + m_TextWidgetShader.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader)); + } + return STextRenderHelper(m_TextWidgetShader, *m_QuadInputAssembler); + } + + STextRenderHelper Qt3DSRendererImpl::GetOnscreenTextShader() + { + if (m_TextOnscreenShader.HasGeneratedShader()) + return STextRenderHelper(m_TextOnscreenShader, *m_QuadStripInputAssembler); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddUniform("vertex_offsets", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + + vertexGenerator.Append("\tvec3 pos = attr_pos + vec3(vertex_offsets, 0.0);"); + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(pos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat alpha = texture2D( text_image, uv_coords ).a;"); + fragmentGenerator.Append( + "\tfragOutput = vec4(text_textcolor.r, text_textcolor.g, text_textcolor.b, alpha);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "onscreen texture shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (theShader == NULL) + m_TextOnscreenShader.Set(NULL); + else { + GenerateXYQuadStrip(); + m_TextOnscreenShader.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader)); + } + return STextRenderHelper(m_TextOnscreenShader, *m_QuadStripInputAssembler); + } + + NVRenderShaderProgram *Qt3DSRendererImpl::GetTextAtlasEntryShader() + { + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat alpha = texture2D( text_image, uv_coords ).a;"); + fragmentGenerator.Append("\tfragOutput = vec4(alpha, alpha, alpha, alpha);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "texture atlas entry shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + return theShader; + } + + STextRenderHelper Qt3DSRendererImpl::GetShader(STextRenderable & /*inRenderable*/, + bool inUsePathRendering) + { + return GetTextShader(inUsePathRendering); + } + + SLayerSceneShader *Qt3DSRendererImpl::GetSceneLayerShader() + { + if (m_SceneLayerShader.hasValue()) + return m_SceneLayerShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("layer_dimensions", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator << "\tvec3 layerPos = vec3(attr_pos.x * layer_dimensions.x / 2.0" + << ", attr_pos.y * layer_dimensions.y / 2.0" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(layerPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("layer_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;\n"); + fragmentGenerator.Append("\tvec4 theLayerTexture = texture2D( layer_image, theCoords );\n"); + fragmentGenerator.Append("\tif( theLayerTexture.a == 0.0 ) discard;\n"); + fragmentGenerator.Append("\tfragOutput = theLayerTexture;\n"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "layer shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SLayerSceneShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SLayerSceneShader)(*theShader); + m_SceneLayerShader = retval; + return m_SceneLayerShader.getValue(); + } + + SLayerProgAABlendShader *Qt3DSRendererImpl::GetLayerProgAABlendShader() + { + if (m_LayerProgAAShader.hasValue()) + return m_LayerProgAAShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + fragmentGenerator.AddUniform("accumulator", "sampler2D"); + fragmentGenerator.AddUniform("last_frame", "sampler2D"); + fragmentGenerator.AddUniform("blend_factors", "vec2"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 accum = texture2D( accumulator, uv_coords );"); + fragmentGenerator.Append("\tvec4 lastFrame = texture2D( last_frame, uv_coords );"); + fragmentGenerator.Append( + "\tgl_FragColor = accum*blend_factors.y + lastFrame*blend_factors.x;"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "layer progressiveAA blend shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SLayerProgAABlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SLayerProgAABlendShader)(*theShader); + m_LayerProgAAShader = retval; + return m_LayerProgAAShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetCubeShadowBlurXShader() + { + if (m_CubeShadowBlurXShader.hasValue()) + return m_CubeShadowBlurXShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + // vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_pos.xy;"); + vertexGenerator.Append("}"); + + // This with the ShadowBlurYShader design for a 2-pass 5x5 (sigma=1.0) + // Weights computed using -- http://dev.theomader.com/gaussian-kernel-calculator/ + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthCube", "samplerCube"); + // fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("layout(location = 0) out vec4 frag0;"); + fragmentGenerator.Append("layout(location = 1) out vec4 frag1;"); + fragmentGenerator.Append("layout(location = 2) out vec4 frag2;"); + fragmentGenerator.Append("layout(location = 3) out vec4 frag3;"); + fragmentGenerator.Append("layout(location = 4) out vec4 frag4;"); + fragmentGenerator.Append("layout(location = 5) out vec4 frag5;"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat ofsScale = camera_properties.x / 2500.0;"); + fragmentGenerator.Append("\tvec3 dir0 = vec3(1.0, -uv_coords.y, -uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir1 = vec3(-1.0, -uv_coords.y, uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir2 = vec3(uv_coords.x, 1.0, uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir3 = vec3(uv_coords.x, -1.0, -uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir4 = vec3(uv_coords.x, -uv_coords.y, 1.0);"); + fragmentGenerator.Append("\tvec3 dir5 = vec3(-uv_coords.x, -uv_coords.y, -1.0);"); + fragmentGenerator.Append("\tfloat depth0;"); + fragmentGenerator.Append("\tfloat depth1;"); + fragmentGenerator.Append("\tfloat depth2;"); + fragmentGenerator.Append("\tfloat outDepth;"); + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir0).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir0 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir0 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir0 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir0 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag0 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir1).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir1 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir1 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir1 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir1 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag1 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir2).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir2 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir2 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir2 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir2 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag2 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir3).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir3 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir3 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir3 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir3 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag3 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir4).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir4 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir4 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir4 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir4 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag4 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir5).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir5 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir5 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir5 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir5 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag5 = vec4(outDepth);"); + + fragmentGenerator.Append("}"); + + CRegisteredString featureName(m_StringTable->RegisterStr("NO_FRAG_OUTPUT")); + SShaderPreprocessorFeature noFragOutputFeature(featureName, true); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "cubemap shadow blur X shader", SShaderCacheProgramFlags(), + TShaderFeatureSet(&noFragOutputFeature, 1)); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_CubeShadowBlurXShader = retval; + return m_CubeShadowBlurXShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetCubeShadowBlurYShader() + { + if (m_CubeShadowBlurYShader.hasValue()) + return m_CubeShadowBlurYShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + // vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_pos.xy;"); + vertexGenerator.Append("}"); + + // This with the ShadowBlurXShader design for a 2-pass 5x5 (sigma=1.0) + // Weights computed using -- http://dev.theomader.com/gaussian-kernel-calculator/ + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthCube", "samplerCube"); + // fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("layout(location = 0) out vec4 frag0;"); + fragmentGenerator.Append("layout(location = 1) out vec4 frag1;"); + fragmentGenerator.Append("layout(location = 2) out vec4 frag2;"); + fragmentGenerator.Append("layout(location = 3) out vec4 frag3;"); + fragmentGenerator.Append("layout(location = 4) out vec4 frag4;"); + fragmentGenerator.Append("layout(location = 5) out vec4 frag5;"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat ofsScale = camera_properties.x / 2500.0;"); + fragmentGenerator.Append("\tvec3 dir0 = vec3(1.0, -uv_coords.y, -uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir1 = vec3(-1.0, -uv_coords.y, uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir2 = vec3(uv_coords.x, 1.0, uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir3 = vec3(uv_coords.x, -1.0, -uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir4 = vec3(uv_coords.x, -uv_coords.y, 1.0);"); + fragmentGenerator.Append("\tvec3 dir5 = vec3(-uv_coords.x, -uv_coords.y, -1.0);"); + fragmentGenerator.Append("\tfloat depth0;"); + fragmentGenerator.Append("\tfloat depth1;"); + fragmentGenerator.Append("\tfloat depth2;"); + fragmentGenerator.Append("\tfloat outDepth;"); + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir0).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir0 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir0 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir0 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir0 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag0 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir1).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir1 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir1 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir1 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir1 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag1 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir2).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir2 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir2 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir2 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir2 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag2 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir3).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir3 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir3 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir3 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir3 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag3 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir4).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir4 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir4 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir4 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir4 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag4 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir5).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir5 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir5 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir5 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir5 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag5 = vec4(outDepth);"); + + fragmentGenerator.Append("}"); + + CRegisteredString featureName(m_StringTable->RegisterStr("NO_FRAG_OUTPUT")); + SShaderPreprocessorFeature noFragOutputFeature(featureName, true); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "cubemap shadow blur Y shader", SShaderCacheProgramFlags(), + TShaderFeatureSet(&noFragOutputFeature, 1)); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_CubeShadowBlurYShader = retval; + return m_CubeShadowBlurYShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetOrthoShadowBlurXShader() + { + if (m_OrthoShadowBlurXShader.hasValue()) + return m_OrthoShadowBlurXShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_uv.xy;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 ofsScale = vec2( camera_properties.x / 7680.0, 0.0 );"); + fragmentGenerator.Append("\tfloat depth0 = texture(depthSrc, uv_coords).x;"); + fragmentGenerator.Append("\tfloat depth1 = texture(depthSrc, uv_coords + ofsScale).x;"); + fragmentGenerator.Append("\tdepth1 += texture(depthSrc, uv_coords - ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat depth2 = texture(depthSrc, uv_coords + 2.0 * ofsScale).x;"); + fragmentGenerator.Append("\tdepth2 += texture(depthSrc, uv_coords - 2.0 * ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat outDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfragOutput = vec4(outDepth);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "shadow map blur X shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_OrthoShadowBlurXShader = retval; + return m_OrthoShadowBlurXShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetOrthoShadowBlurYShader() + { + if (m_OrthoShadowBlurYShader.hasValue()) + return m_OrthoShadowBlurYShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_uv.xy;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 ofsScale = vec2( 0.0, camera_properties.x / 7680.0 );"); + fragmentGenerator.Append("\tfloat depth0 = texture(depthSrc, uv_coords).x;"); + fragmentGenerator.Append("\tfloat depth1 = texture(depthSrc, uv_coords + ofsScale).x;"); + fragmentGenerator.Append("\tdepth1 += texture(depthSrc, uv_coords - ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat depth2 = texture(depthSrc, uv_coords + 2.0 * ofsScale).x;"); + fragmentGenerator.Append("\tdepth2 += texture(depthSrc, uv_coords - 2.0 * ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat outDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfragOutput = vec4(outDepth);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "shadow map blur Y shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_OrthoShadowBlurYShader = retval; + return m_OrthoShadowBlurYShader.getValue(); + } + +#ifdef ADVANCED_BLEND_SW_FALLBACK + SAdvancedModeBlendShader * + Qt3DSRendererImpl::GetAdvancedBlendModeShader(AdvancedBlendModes::Enum blendMode) + { + // Select between blend equations. + if (blendMode == AdvancedBlendModes::Overlay) { + return GetOverlayBlendModeShader(); + } else if (blendMode == AdvancedBlendModes::ColorBurn) { + return GetColorBurnBlendModeShader(); + } else if (blendMode == AdvancedBlendModes::ColorDodge) { + return GetColorDodgeBlendModeShader(); + } + return {}; + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetOverlayBlendModeShader() + { + if (m_AdvancedModeOverlayBlendShader.hasValue()) + return m_AdvancedModeOverlayBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = (base.r < 0.5? (2.0 * base.r * blend.r) : " + "(1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = (base.g < 0.5? (2.0 * base.g * blend.g) : " + "(1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = (base.b < 0.5? (2.0 * base.b * blend.b) : " + "(1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced overlay shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeOverlayBlendShader = retval; + return m_AdvancedModeOverlayBlendShader.getValue(); + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetColorBurnBlendModeShader() + { + if (m_AdvancedModeColorBurnBlendShader.hasValue()) + return m_AdvancedModeColorBurnBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = ((base.r == 1.0) ? 1.0 : " + "(blend.r == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.r) / blend.r)));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = ((base.g == 1.0) ? 1.0 : " + "(blend.g == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.g) / blend.g)));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = ((base.b == 1.0) ? 1.0 : " + "(blend.b == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.b) / blend.b)));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced colorBurn shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeColorBurnBlendShader = retval; + return m_AdvancedModeColorBurnBlendShader.getValue(); + + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetColorDodgeBlendModeShader() + { + if (m_AdvancedModeColorDodgeBlendShader.hasValue()) + return m_AdvancedModeColorDodgeBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = ((base.r == 0.0) ? 0.0 : " + "(blend.r == 1.0) ? 1.0 : min(base.r / (1.0 - blend.r), 1.0));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = ((base.g == 0.0) ? 0.0 : " + "(blend.g == 1.0) ? 1.0 : min(base.g / (1.0 - blend.g), 1.0));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = ((base.b == 0.0) ? 0.0 : " + "(blend.b == 1.0) ? 1.0 : min(base.b / (1.0 - blend.b), 1.0));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced colorDodge shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeColorDodgeBlendShader = retval; + return m_AdvancedModeColorDodgeBlendShader.getValue(); + + } +#endif +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h new file mode 100644 index 0000000..1ac85db --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_SHADERS_H +#define QT3DS_RENDERER_IMPL_SHADERS_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "render/Qt3DSRenderProgramPipeline.h" + +namespace qt3ds { +namespace render { + using qt3ds::render::NVRenderCachedShaderProperty; + using qt3ds::render::NVRenderCachedShaderBuffer; + + /** + * Cached tessellation property lookups this is on a per mesh base + */ + struct SShaderTessellationProperties + { + NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessLevel; ///< tesselation value for the edges + NVRenderCachedShaderProperty<QT3DSF32> m_InsideTessLevel; ///< tesselation value for the inside + NVRenderCachedShaderProperty<QT3DSF32> + m_PhongBlend; ///< blending between linear and phong component + NVRenderCachedShaderProperty<QT3DSVec2> + m_DistanceRange; ///< distance range for min and max tess level + NVRenderCachedShaderProperty<QT3DSF32> m_DisableCulling; ///< if set to 1.0 this disables + ///backface culling optimization in + ///the tess shader + + SShaderTessellationProperties() {} + SShaderTessellationProperties(NVRenderShaderProgram &inShader) + : m_EdgeTessLevel("tessLevelOuter", inShader) + , m_InsideTessLevel("tessLevelInner", inShader) + , m_PhongBlend("phongBlend", inShader) + , m_DistanceRange("distanceRange", inShader) + , m_DisableCulling("disableCulling", inShader) + { + } + }; + + /** + * The results of generating a shader. Caches all possible variable names into + * typesafe objects. + */ + struct SShaderGeneratorGeneratedShader + { + QT3DSU32 m_LayerSetIndex; + CRegisteredString m_QueryString; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewportMatrix; + SShaderTessellationProperties m_Tessellation; + + SShaderGeneratorGeneratedShader(CRegisteredString inQueryString, + NVRenderShaderProgram &inShader) + : m_LayerSetIndex(QT3DS_MAX_U32) + , m_QueryString(inQueryString) + , m_Shader(inShader) + , m_ViewportMatrix("viewport_matrix", inShader) + , m_Tessellation(inShader) + { + m_Shader.addRef(); + } + ~SShaderGeneratorGeneratedShader() { m_Shader.release(); } + static QT3DSU32 GetLayerIndex(const SShaderGeneratorGeneratedShader &inShader) + { + return inShader.m_LayerSetIndex; + } + static void SetLayerIndex(SShaderGeneratorGeneratedShader &inShader, QT3DSU32 idx) + { + inShader.m_LayerSetIndex = idx; + } + }; + + struct SDefaultMaterialRenderableDepthShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + + QT3DSI32 m_RefCount; + SDefaultMaterialRenderableDepthShader(NVRenderShaderProgram &inShader, + NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_RefCount(0) + { + m_Shader.addRef(); + } + + ~SDefaultMaterialRenderableDepthShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + /** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ + struct SShaderTextureProperties + { + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + { + } + SShaderTextureProperties() {} + }; + + struct SRenderableDepthPrepassShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + NVRenderCachedShaderProperty<QT3DSMat44> m_GlobalTransform; + NVRenderCachedShaderProperty<QT3DSMat44> m_Projection; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition; + NVRenderCachedShaderProperty<QT3DSF32> m_DisplaceAmount; + SShaderTextureProperties m_DisplacementProps; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + // NVRenderCachedShaderProperty<QT3DSMat44> m_ShadowMV[6]; + + QT3DSI32 m_RefCount; + // Cache the tessellation property name lookups + SShaderTessellationProperties m_Tessellation; + + SRenderableDepthPrepassShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_GlobalTransform("model_matrix", inShader) + , m_Projection("projection", inShader) + , m_CameraPosition("camera_position", inShader) + , m_DisplaceAmount("displaceAmount", inShader) + , m_DisplacementProps("displacementSampler", "displacementMap_offset", + "displacementMap_rot", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_RefCount(0) + , m_Tessellation(inShader) + { + /* + m_ShadowMV[0].m_Shader = &inShader; + m_ShadowMV[0].m_Constant = inShader.GetShaderConstant( "shadow_mv0" ); + m_ShadowMV[1].m_Shader = &inShader; + m_ShadowMV[1].m_Constant = inShader.GetShaderConstant( "shadow_mv1" ); + m_ShadowMV[2].m_Shader = &inShader; + m_ShadowMV[2].m_Constant = inShader.GetShaderConstant( "shadow_mv2" ); + m_ShadowMV[3].m_Shader = &inShader; + m_ShadowMV[3].m_Constant = inShader.GetShaderConstant( "shadow_mv3" ); + m_ShadowMV[4].m_Shader = &inShader; + m_ShadowMV[4].m_Constant = inShader.GetShaderConstant( "shadow_mv4" ); + m_ShadowMV[5].m_Shader = &inShader; + m_ShadowMV[5].m_Constant = inShader.GetShaderConstant( "shadow_mv5" ); + */ + m_Shader.addRef(); + } + + ~SRenderableDepthPrepassShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct SDefaultAoPassShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_CubeTexture; + NVRenderCachedShaderProperty<QT3DSVec2> m_DepthSamplerSize; + + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + QT3DSI32 m_RefCount; + + SDefaultAoPassShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_ViewMatrix("view_matrix", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_CubeTexture("depth_cube", inShader) + , m_DepthSamplerSize("depth_sampler_size", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~SDefaultAoPassShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct STextShader + { + NVRenderShaderProgram &m_Shader; + + NVScopedRefCounted<NVRenderProgramPipeline> m_ProgramPipeline; + + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec4> m_Dimensions; + // The fourth member of text color is the opacity + NVRenderCachedShaderProperty<QT3DSVec4> m_TextColor; + NVRenderCachedShaderProperty<QT3DSVec3> m_BackgroundColor; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + // Dimensions and offsetting of the texture + NVRenderCachedShaderProperty<QT3DSVec3> m_TextDimensions; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + // Used only for onscreen text + NVRenderCachedShaderProperty<QT3DSVec2> m_VertexOffsets; + + STextShader(NVRenderShaderProgram &shader, NVRenderProgramPipeline *pipeline = NULL) + : m_Shader(shader) + , m_ProgramPipeline(pipeline) + , m_MVP("model_view_projection", shader) + , m_Dimensions("text_dimensions", shader) + , m_TextColor("text_textcolor", shader) + , m_BackgroundColor("text_backgroundcolor", shader) + , m_Sampler("text_image", shader) + , m_TextDimensions("text_textdimensions", shader) + , m_CameraProperties("camera_properties", shader) + , m_VertexOffsets("vertex_offsets", shader) + { + if (!pipeline) + m_Shader.addRef(); + } + ~STextShader() + { + if (!m_ProgramPipeline.mPtr) + m_Shader.release(); + } + void Render(NVRenderTexture2D &inTexture, const STextScaleAndOffset &inScaleAndOffset, + const QT3DSVec4 &inTextColor, const QT3DSMat44 &inMVP, const QT3DSVec2 &inCameraVec, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor); + + void RenderPath(NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inPathFontSpec, + const STextScaleAndOffset &inScaleAndOffset, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inViewProjection, const QT3DSMat44 &inModel, + const QT3DSVec2 &inCameraVec, NVRenderContext &inRenderContext, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor); + + void Render2D(NVRenderTexture2D &inTexture, const QT3DSVec4 &inTextColor, const QT3DSMat44 &inMVP, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + QT3DSVec2 inVertexOffsets); + }; + + struct STextDepthShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec4> m_Dimensions; + NVRenderCachedShaderProperty<QT3DSVec3> m_TextDimensions; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderInputAssembler &m_QuadInputAssembler; + QT3DSI32 m_RefCount; + + STextDepthShader(NVAllocatorCallback &alloc, NVRenderShaderProgram &prog, + NVRenderInputAssembler &assembler) + : m_Allocator(alloc) + , m_Shader(prog) + , m_MVP("model_view_projection", prog) + , m_Dimensions("text_dimensions", prog) + , m_TextDimensions("text_textdimensions", prog) + , m_CameraProperties("camera_properties", prog) + , m_Sampler("text_image", prog) + , m_QuadInputAssembler(assembler) + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~STextDepthShader() { m_Shader.release(); } + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct SLayerProgAABlendShader + { + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AccumSampler; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LastFrame; + NVRenderCachedShaderProperty<QT3DSVec2> m_BlendFactors; + volatile QT3DSI32 mRefCount; + SLayerProgAABlendShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_AccumSampler("accumulator", inShader) + , m_LastFrame("last_frame", inShader) + , m_BlendFactors("blend_factors", inShader) + , mRefCount(0) + { + } + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + }; + + struct SLayerSceneShader + { + NVRenderShaderProgram &m_Shader; + + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec2> m_Dimensions; + // The fourth member of text color is the opacity + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + + volatile QT3DSI32 mRefCount; + + SLayerSceneShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_Dimensions("layer_dimensions", inShader) + , m_Sampler("layer_image", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SLayerSceneShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + }; + + struct SShadowmapPreblurShader + { + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_DepthCube; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthMap; + + volatile QT3DSI32 mRefCount; + + SShadowmapPreblurShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_CameraProperties("camera_properties", inShader) + , m_DepthCube("depthCube", inShader) + , m_DepthMap("depthSrc", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SShadowmapPreblurShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + }; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + struct SAdvancedModeBlendShader + { + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_baseLayer; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_blendLayer; + + volatile QT3DSI32 mRefCount; + + SAdvancedModeBlendShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_baseLayer("base_layer", inShader) + , m_blendLayer("blend_layer", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SAdvancedModeBlendShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + + }; +#endif + + struct SGGSGet + { + QT3DSU32 operator()(const SShaderGeneratorGeneratedShader &inShader) + { + return inShader.m_LayerSetIndex; + } + }; + struct SGGSSet + { + void operator()(SShaderGeneratorGeneratedShader &inShader, QT3DSU32 idx) + { + inShader.m_LayerSetIndex = idx; + } + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h b/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h new file mode 100644 index 0000000..a9b5e7b --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_VERTEX_PIPELINE_IMPL_H +#define QT3DS_VERTEX_PIPELINE_IMPL_H +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +namespace qt3ds { +namespace render { + // Baseclass for the vertex pipelines to be sure we have consistent implementations. + struct SVertexPipelineImpl : public IDefaultMaterialVertexPipeline + { + struct GenerationFlagValues + { + enum Enum { + UVCoords = 1, + EnvMapReflection = 1 << 1, + ViewVector = 1 << 2, + WorldNormal = 1 << 3, + ObjectNormal = 1 << 4, + WorldPosition = 1 << 5, + TangentBinormal = 1 << 6, + UVCoords1 = 1 << 7, + VertexColor = 1 << 8, + }; + }; + + typedef TStrTableStrMap::const_iterator TParamIter; + typedef NVFlags<GenerationFlagValues::Enum> TGenerationFlags; + + IMaterialShaderGenerator &m_MaterialGenerator; + IShaderProgramGenerator &m_ProgramGenerator; + IStringTable &m_StringTable; + Qt3DSString m_TempString; + + TGenerationFlags m_GenerationFlags; + bool m_Wireframe; + TStrTableStrMap m_InterpolationParameters; + QT3DSU32 m_DisplacementIdx; + SRenderableImage *m_DisplacementImage; + QStringList m_addedFunctions; + + SVertexPipelineImpl(NVAllocatorCallback &inAllocator, IMaterialShaderGenerator &inMaterial, + IShaderProgramGenerator &inProgram, IStringTable &inStringTable, + bool inWireframe // only works if tessellation is true + ) + + : m_MaterialGenerator(inMaterial) + , m_ProgramGenerator(inProgram) + , m_StringTable(inStringTable) + , m_Wireframe(inWireframe) + , m_InterpolationParameters(inAllocator, "m_InterpolationParameters") + , m_DisplacementIdx(0) + , m_DisplacementImage(NULL) + { + } + + // Trues true if the code was *not* set. + bool SetCode(GenerationFlagValues::Enum inCode) + { + if (((QT3DSU32)m_GenerationFlags & inCode) != 0) + return true; + m_GenerationFlags |= inCode; + return false; + } + bool HasCode(GenerationFlagValues::Enum inCode) + { + return ((QT3DSU32)(m_GenerationFlags & inCode)) != 0; + } + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IShaderStageGenerator &Vertex() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Vertex); + } + IShaderStageGenerator &TessControl() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl); + } + IShaderStageGenerator &TessEval() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval); + } + IShaderStageGenerator &Geometry() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry); + } + IShaderStageGenerator &Fragment() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Fragment); + } + IMaterialShaderGenerator &MaterialGenerator() { return m_MaterialGenerator; } + + void SetupDisplacement(QT3DSU32 displacementImageIdx, SRenderableImage *displacementImage) + { + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + } + + CRegisteredString Str(const char8_t *inItem) { return m_StringTable.RegisterStr(inItem); } + + bool HasTessellation() const + { + return m_ProgramGenerator.GetEnabledStages() & ShaderGeneratorStages::TessEval; + } + bool HasGeometryStage() const + { + return m_ProgramGenerator.GetEnabledStages() & ShaderGeneratorStages::Geometry; + } + bool HasDisplacment() const { return m_DisplacementImage != NULL; } + + void InitializeWireframeGeometryShader() + { + if (m_Wireframe && ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) + && ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)) { + IShaderStageGenerator &geometryShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry)); + // currently geometry shader is only used for drawing wireframe + if (m_Wireframe) { + geometryShader.AddUniform("viewport_matrix", "mat4"); + geometryShader.AddOutgoing("varEdgeDistance", "vec3"); + geometryShader.Append("layout (triangles) in;"); + geometryShader.Append("layout (triangle_strip, max_vertices = 3) out;"); + geometryShader.Append("void main() {"); + + // how this all work see + // http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf + + geometryShader.Append( + "// project points to screen space\n" + "\tvec3 p0 = vec3(viewport_matrix * (gl_in[0].gl_Position / " + "gl_in[0].gl_Position.w));\n" + "\tvec3 p1 = vec3(viewport_matrix * (gl_in[1].gl_Position / " + "gl_in[1].gl_Position.w));\n" + "\tvec3 p2 = vec3(viewport_matrix * (gl_in[2].gl_Position / " + "gl_in[2].gl_Position.w));\n" + "// compute triangle heights\n" + "\tfloat e1 = length(p1 - p2);\n" + "\tfloat e2 = length(p2 - p0);\n" + "\tfloat e3 = length(p1 - p0);\n" + "\tfloat alpha = acos( (e2*e2 + e3*e3 - e1*e1) / (2.0*e2*e3) );\n" + "\tfloat beta = acos( (e1*e1 + e3*e3 - e2*e2) / (2.0*e1*e3) );\n" + "\tfloat ha = abs( e3 * sin( beta ) );\n" + "\tfloat hb = abs( e3 * sin( alpha ) );\n" + "\tfloat hc = abs( e2 * sin( alpha ) );\n"); + } + } + } + + void FinalizeWireframeGeometryShader() + { + IShaderStageGenerator &geometryShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry)); + + if (m_Wireframe == true && ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) + && ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)) { + const char8_t *theExtension("TE["); + // we always assume triangles + for (int i = 0; i < 3; i++) { + char buf[10]; + sprintf(buf, "%d", i); + for (TStrTableStrMap::const_iterator iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + geometryShader << "\t" << iter->first.c_str() << " = " + << iter->first.c_str() << theExtension << buf << "];\n"; + } + + geometryShader << "\tgl_Position = gl_in[" << buf << "].gl_Position;\n"; + // the triangle distance is interpolated through the shader stage + if (i == 0) + geometryShader << "\n\tvarEdgeDistance = vec3(ha*" + << "gl_in[" << buf << "].gl_Position.w, 0.0, 0.0);\n"; + else if (i == 1) + geometryShader << "\n\tvarEdgeDistance = vec3(0.0, hb*" + << "gl_in[" << buf << "].gl_Position.w, 0.0);\n"; + else if (i == 2) + geometryShader << "\n\tvarEdgeDistance = vec3(0.0, 0.0, hc*" + << "gl_in[" << buf << "].gl_Position.w);\n"; + + // submit vertex + geometryShader << "\tEmitVertex();\n"; + } + // end primitive + geometryShader << "\tEndPrimitive();\n"; + } + } + + virtual void SetupTessIncludes(ShaderGeneratorStages::Enum inStage, + TessModeValues::Enum inTessMode) + { + IShaderStageGenerator &tessShader(*ProgramGenerator().GetStage(inStage)); + + // depending on the selected tessellation mode chose program + switch (inTessMode) { + case TessModeValues::TessPhong: + tessShader.AddInclude("tessellationPhong.glsllib"); + break; + case TessModeValues::TessNPatch: + tessShader.AddInclude("tessellationNPatch.glsllib"); + break; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case TessModeValues::TessLinear: + tessShader.AddInclude("tessellationLinear.glsllib"); + break; + } + } + + void GenerateUVCoords(QT3DSU32 inUVSet = 0) override + { + if (inUVSet == 0 && SetCode(GenerationFlagValues::UVCoords)) + return; + if (inUVSet == 1 && SetCode(GenerationFlagValues::UVCoords1)) + return; + + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) + AddInterpolationParameter("varTexCoord0", "vec2"); + else if (inUVSet == 1) + AddInterpolationParameter("varTexCoord1", "vec2"); + + DoGenerateUVCoords(inUVSet); + } + void GenerateEnvMapReflection() override + { + if (SetCode(GenerationFlagValues::EnvMapReflection)) + return; + + GenerateWorldPosition(); + GenerateWorldNormal(); + IShaderStageGenerator &activeGenerator(ActiveStage()); + activeGenerator.AddInclude("viewProperties.glsllib"); + AddInterpolationParameter("var_object_to_camera", "vec3"); + activeGenerator.Append("\tvar_object_to_camera = normalize( local_model_world_position " + "- camera_position );"); + // World normal cannot be relied upon in the vertex shader because of bump maps. + Fragment().Append("\tvec3 environment_map_reflection = reflect( " + "normalize(var_object_to_camera), world_normal.xyz );"); + Fragment().Append("\tenvironment_map_reflection *= vec3( 0.5, 0.5, 0 );"); + Fragment().Append("\tenvironment_map_reflection += vec3( 0.5, 0.5, 1.0 );"); + } + void GenerateViewVector() override + { + if (SetCode(GenerationFlagValues::ViewVector)) + return; + GenerateWorldPosition(); + IShaderStageGenerator &activeGenerator(ActiveStage()); + activeGenerator.AddInclude("viewProperties.glsllib"); + AddInterpolationParameter("varViewVector", "vec3"); + activeGenerator.Append("\tvec3 local_view_vector = normalize(camera_position - " + "local_model_world_position);"); + AssignOutput("varViewVector", "local_view_vector"); + Fragment() << "\tvec3 view_vector = normalize(varViewVector);" << Endl; + } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void GenerateWorldNormal() override + { + if (SetCode(GenerationFlagValues::WorldNormal)) + return; + AddInterpolationParameter("varNormal", "vec3"); + DoGenerateWorldNormal(); + Fragment().Append("\tvec3 world_normal = normalize( varNormal );"); + } + void GenerateObjectNormal() override + { + if (SetCode(GenerationFlagValues::ObjectNormal)) + return; + DoGenerateObjectNormal(); + Fragment().Append("\tvec3 object_normal = normalize(varObjectNormal);"); + } + void GenerateWorldPosition() override + { + if (SetCode(GenerationFlagValues::WorldPosition)) + return; + + ActiveStage().AddUniform("model_matrix", "mat4"); + AddInterpolationParameter("varWorldPos", "vec3"); + DoGenerateWorldPosition(); + + AssignOutput("varWorldPos", "local_model_world_position"); + } + void GenerateVarTangentAndBinormal() override + { + if (SetCode(GenerationFlagValues::TangentBinormal)) + return; + AddInterpolationParameter("varTangent", "vec3"); + AddInterpolationParameter("varBinormal", "vec3"); + DoGenerateVarTangentAndBinormal(); + Fragment() << "\tvec3 tangent = normalize(varTangent);" << Endl + << "\tvec3 binormal = normalize(varBinormal);" << Endl; + } + void GenerateVertexColor() override + { + if (SetCode(GenerationFlagValues::VertexColor)) + return; + AddInterpolationParameter("varColor", "vec3"); + DoGenerateVertexColor(); + Fragment().Append("\tvec3 vertColor = varColor;"); + } + + bool HasActiveWireframe() override { return m_Wireframe; } + + // IShaderStageGenerator interface + void AddIncoming(const char8_t *name, const char8_t *type) override + { + ActiveStage().AddIncoming(name, type); + } + void AddIncoming(const TStrType &name, const char8_t *type) override + { + AddIncoming(name.c_str(), type); + } + void AddIncoming(const QString &name, const char8_t *type) override + { + AddIncoming(name.toUtf8().constData(), type); + } + + void AddOutgoing(const char8_t *name, const char8_t *type) override + { + AddInterpolationParameter(name, type); + } + void AddOutgoing(const TStrType &name, const char8_t *type) override + { + AddOutgoing(name.c_str(), type); + } + void AddOutgoing(const QString &name, const char8_t *type) override + { + AddOutgoing(name.toUtf8().constData(), type); + } + void AddUniform(const QString &name, const char8_t *type) override + { + AddUniform(name.toUtf8().constData(), type); + } + + void AddUniform(const char8_t *name, const char8_t *type) override + { + ActiveStage().AddUniform(name, type); + } + void AddUniform(const TStrType &name, const char8_t *type) override + { + AddUniform(name.c_str(), type); + } + + void AddInclude(const char8_t *name) override { ActiveStage().AddInclude(name); } + void AddInclude(const TStrType &name) override { AddInclude(name.c_str()); } + void AddInclude(const QString &name) override + { + QByteArray arr = name.toLatin1(); + AddInclude(arr.data()); + } + + void AddFunction(const QString &functionName) override + { + if (!m_addedFunctions.contains(functionName)) { + m_addedFunctions.push_back(functionName); + QString includeName; + QTextStream stream(&includeName); + stream << "func" << functionName << ".glsllib"; + AddInclude(includeName); + } + } + + void AddConstantBuffer(const char *name, const char *layout) override + { + ActiveStage().AddConstantBuffer(name, layout); + } + void AddConstantBuffer(const QString &name, const char *layout) override + { + AddConstantBuffer(name.toUtf8().constData(), layout); + } + + void AddConstantBufferParam(const char *cbName, const char *paramName, + const char *type) override + { + ActiveStage().AddConstantBufferParam(cbName, paramName, type); + } + void AddConstantBufferParam(const QString &cbName, const QString ¶mName, + const char *type) override + { + AddConstantBufferParam(cbName.toUtf8().constData(), + paramName.toUtf8().constData(), type); + } + + IShaderStageGenerator &operator<<(const char *data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const TStrType &data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const QString &data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const SEndlType &data) override + { + ActiveStage() << data; + return *this; + } + void Append(const char *data) override { ActiveStage().Append(data); } + void AppendPartial(const char *data) override { ActiveStage().Append(data); } + + ShaderGeneratorStages::Enum Stage() const override + { + return const_cast<SVertexPipelineImpl *>(this)->ActiveStage().Stage(); + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override = 0; + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValueExpr) override = 0; + void EndVertexGeneration(bool customShader) override = 0; + + void BeginFragmentGeneration() override = 0; + void EndFragmentGeneration(bool customShader) override = 0; + + virtual IShaderStageGenerator &ActiveStage() = 0; + virtual void AddInterpolationParameter(const char8_t *inParamName, + const char8_t *inParamType) = 0; + + virtual void DoGenerateUVCoords(QT3DSU32 inUVSet) = 0; + virtual void DoGenerateWorldNormal() = 0; + virtual void DoGenerateObjectNormal() = 0; + virtual void DoGenerateWorldPosition() = 0; + virtual void DoGenerateVarTangentAndBinormal() = 0; + virtual void DoGenerateVertexColor() = 0; + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp new file mode 100644 index 0000000..963186f --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderBufferLoader.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSSync.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "Qt3DSRenderThreadPool.h" + +using namespace qt3ds::render; + +namespace { +struct SBufferLoader; +struct SBufferLoadResult : public ILoadedBuffer +{ + NVFoundationBase &m_Foundation; + CRegisteredString m_Path; + IBufferLoaderCallback *m_UserData; + NVDataRef<QT3DSU8> m_Data; + QT3DSI32 mRefCount; + + SBufferLoadResult(NVFoundationBase &fnd, CRegisteredString p, IBufferLoaderCallback *ud, + NVDataRef<QT3DSU8> data) + : m_Foundation(fnd) + , m_Path(p) + , m_UserData(ud) + , m_Data(data) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + CRegisteredString Path() override { return m_Path; } + // Data is released when the buffer itself is released. + NVDataRef<QT3DSU8> Data() override { return m_Data; } + IBufferLoaderCallback *UserData() override { return m_UserData; } + + static SBufferLoadResult *Allocate(QT3DSU32 inBufferSize, NVFoundationBase &fnd, + CRegisteredString p, + NVScopedRefCounted<IBufferLoaderCallback> ud) + { + size_t allocSize = sizeof(SBufferLoadResult) + inBufferSize; + QT3DSU8 *allocMem = + (QT3DSU8 *)fnd.getAllocator().allocate(allocSize, "ILoadedBuffer", __FILE__, __LINE__); + if (allocMem == NULL) + return NULL; + QT3DSU8 *bufferStart = allocMem + sizeof(SBufferLoadResult); + NVDataRef<QT3DSU8> dataBuffer = toDataRef(bufferStart, inBufferSize); + return new (allocMem) SBufferLoadResult(fnd, p, ud, dataBuffer); + } +}; +struct SLoadedBufferImpl +{ + SBufferLoader &m_Loader; + QT3DSU64 m_JobId; + QT3DSU64 m_LoadId; + CRegisteredString m_Path; + NVScopedRefCounted<IBufferLoaderCallback> m_UserData; + bool m_Quiet; + volatile bool m_Cancel; + NVScopedRefCounted<SBufferLoadResult> m_Result; + SLoadedBufferImpl *m_NextBuffer; + SLoadedBufferImpl *m_PreviousBuffer; + + SLoadedBufferImpl(SBufferLoader &l, CRegisteredString inPath, + NVScopedRefCounted<IBufferLoaderCallback> ud, bool inQuiet, QT3DSU64 loadId) + : m_Loader(l) + , m_JobId(0) + , m_LoadId(loadId) + , m_Path(inPath) + , m_UserData(ud) + , m_Quiet(inQuiet) + , m_Cancel(false) + , m_NextBuffer(NULL) + , m_PreviousBuffer(NULL) + { + } +}; + +DEFINE_INVASIVE_LIST(LoadedBufferImpl); +IMPLEMENT_INVASIVE_LIST(LoadedBufferImpl, m_PreviousBuffer, m_NextBuffer); + +struct SBufferLoader : public IBufferLoader +{ + NVFoundationBase &m_Foundation; + NVScopedRefCounted<IInputStreamFactory> m_Factory; + NVScopedRefCounted<IThreadPool> m_ThreadPool; + + Mutex m_BuffersToLoadMutex; + TLoadedBufferImplList m_BuffersToLoad; + + Mutex m_BuffersLoadingMutex; + TLoadedBufferImplList m_BuffersLoading; + + Mutex m_LoadedBuffersMutex; + TLoadedBufferImplList m_LoadedBuffers; + + Sync m_BufferLoadedEvent; + + QT3DSU64 m_NextBufferId; + + QT3DSI32 mRefCount; + + SBufferLoader(NVFoundationBase &fnd, IInputStreamFactory &fac, IThreadPool &tp) + : m_Foundation(fnd) + , m_Factory(fac) + , m_ThreadPool(tp) + , m_BuffersToLoadMutex(fnd.getAllocator()) + , m_BuffersLoadingMutex(fnd.getAllocator()) + , m_LoadedBuffersMutex(fnd.getAllocator()) + , m_BufferLoadedEvent(fnd.getAllocator()) + , m_NextBufferId(1) + , mRefCount(0) + { + m_BufferLoadedEvent.reset(); + } + + virtual ~SBufferLoader() + { + { + Mutex::ScopedLock __locker(m_BuffersToLoadMutex); + for (TLoadedBufferImplList::iterator iter = m_BuffersToLoad.begin(), + end = m_BuffersToLoad.end(); + iter != end; ++iter) { + m_ThreadPool->CancelTask(iter->m_JobId); + } + } + + // Pull any remaining buffers out of the thread system. + while (WillLoadedBuffersBeAvailable()) { + NextLoadedBuffer(); + } + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + static void InitializeActiveLoadingBuffer(SLoadedBufferImpl &theBuffer) + { + Mutex::ScopedLock theLocker(theBuffer.m_Loader.m_BuffersToLoadMutex); + Mutex::ScopedLock theSecondLocker(theBuffer.m_Loader.m_BuffersLoadingMutex); + theBuffer.m_Loader.m_BuffersToLoad.remove(theBuffer); + theBuffer.m_Loader.m_BuffersLoading.push_back(theBuffer); + } + + static void SetBufferAsLoaded(SLoadedBufferImpl &theBuffer) + { + Mutex::ScopedLock theSecondLocker(theBuffer.m_Loader.m_BuffersLoadingMutex); + Mutex::ScopedLock theLocker(theBuffer.m_Loader.m_LoadedBuffersMutex); + theBuffer.m_Loader.m_BuffersLoading.remove(theBuffer); + theBuffer.m_Loader.m_LoadedBuffers.push_back(theBuffer); + theBuffer.m_Loader.m_BufferLoadedEvent.set(); + theBuffer.m_Loader.m_BufferLoadedEvent.reset(); + } + + static void LoadNextBuffer(void *loader) + { + SLoadedBufferImpl &theBuffer = *reinterpret_cast<SLoadedBufferImpl *>(loader); + + InitializeActiveLoadingBuffer(theBuffer); + NVScopedRefCounted<IRefCountedInputStream> theStream = + theBuffer.m_Loader.m_Factory->GetStreamForFile(theBuffer.m_Path.c_str(), + theBuffer.m_Quiet); + if (theStream && theBuffer.m_Cancel == false) { + theStream->SetPosition(0, SeekPosition::End); + QT3DSI64 theFileLen = theStream->GetPosition(); + if (theFileLen > 0 && theFileLen < (QT3DSU32)QT3DS_MAX_U32) { + QT3DSU32 required = (QT3DSU32)theFileLen; + theBuffer.m_Result = + SBufferLoadResult::Allocate(required, theBuffer.m_Loader.m_Foundation, + theBuffer.m_Path, theBuffer.m_UserData); + QT3DSU32 amountRead = 0; + QT3DSU32 total = amountRead; + if (theBuffer.m_Result && theBuffer.m_Cancel == false) { + NVDataRef<QT3DSU8> theDataBuffer(theBuffer.m_Result->m_Data); + theStream->SetPosition(0, SeekPosition::Begin); + amountRead = theStream->Read(theDataBuffer); + total += amountRead; + // Ensure we keep trying, not all file systems allow huge reads. + while (total < required && amountRead > 0 && theBuffer.m_Cancel == false) { + NVDataRef<QT3DSU8> newBuffer(theDataBuffer.mData + total, required - total); + amountRead = theStream->Read(newBuffer); + total += amountRead; + } + } + if (theBuffer.m_Cancel || total != required) { + theBuffer.m_Result->m_Data = NVDataRef<QT3DSU8>(); + } + } + } + + // only callback if the file was successfully loaded. + if (theBuffer.m_UserData) { + if (theBuffer.m_Cancel == false && theBuffer.m_Result.mPtr + && theBuffer.m_Result->m_Data.size()) { + theBuffer.m_UserData->OnBufferLoaded(*theBuffer.m_Result.mPtr); + } else { + if (theBuffer.m_Cancel) + theBuffer.m_UserData->OnBufferLoadCancelled(theBuffer.m_Path); + else + theBuffer.m_UserData->OnBufferLoadFailed(theBuffer.m_Path); + } + } + + SetBufferAsLoaded(theBuffer); + } + static void CancelNextBuffer(void *loader) + { + SLoadedBufferImpl &theBuffer = *reinterpret_cast<SLoadedBufferImpl *>(loader); + theBuffer.m_Cancel = true; + InitializeActiveLoadingBuffer(theBuffer); + + if (theBuffer.m_UserData) + theBuffer.m_UserData->OnBufferLoadCancelled(theBuffer.m_Path); + + SetBufferAsLoaded(theBuffer); + } + + // nonblocking. Quiet failure is passed to the input stream factory. + QT3DSU64 QueueForLoading(CRegisteredString inPath, + IBufferLoaderCallback *inUserData = NULL, + bool inQuietFailure = false) override + { + SLoadedBufferImpl &theBuffer = *QT3DS_NEW(m_Foundation.getAllocator(), SLoadedBufferImpl)( + *this, inPath, inUserData, inQuietFailure, m_NextBufferId); + ++m_NextBufferId; + { + Mutex::ScopedLock theLocker(m_BuffersToLoadMutex); + m_BuffersToLoad.push_back(theBuffer); + } + theBuffer.m_JobId = m_ThreadPool->AddTask(&theBuffer, LoadNextBuffer, CancelNextBuffer); + return theBuffer.m_LoadId; + } + + void CancelBufferLoad(QT3DSU64 inBufferId) override + { + { + Mutex::ScopedLock theLocker(m_BuffersToLoadMutex); + SLoadedBufferImpl *theLoadedBuffer = NULL; + for (TLoadedBufferImplList::iterator iter = m_BuffersToLoad.begin(), + end = m_BuffersToLoad.end(); + iter != end && theLoadedBuffer == NULL; ++iter) { + if (iter->m_LoadId == inBufferId) { + theLoadedBuffer = &(*iter); + // both cancellation attempts are necessary. The user will still get + // a load result, it will just have no data. + theLoadedBuffer->m_Cancel = true; + m_ThreadPool->CancelTask(theLoadedBuffer->m_JobId); + } + } + } + } + + // If we were will to wait, will we ever get another buffer + bool WillLoadedBuffersBeAvailable() override + { + Mutex::ScopedLock theLocker(m_BuffersToLoadMutex); + Mutex::ScopedLock theSecondLocker(m_BuffersLoadingMutex); + return AreLoadedBuffersAvailable() || m_BuffersToLoad.empty() == false + || m_BuffersLoading.empty() == false; + } + // Will nextLoadedBuffer block or not? + bool AreLoadedBuffersAvailable() override + { + Mutex::ScopedLock theLocker(m_LoadedBuffersMutex); + return m_LoadedBuffers.empty() == false; + } + + // blocking, be careful with this. No order guarantees here. + NVScopedRefCounted<ILoadedBuffer> NextLoadedBuffer() override + { + while (!AreLoadedBuffersAvailable()) { + m_BufferLoadedEvent.wait(); + } + SLoadedBufferImpl *theBuffer; + { + Mutex::ScopedLock theLocker(m_LoadedBuffersMutex); + theBuffer = m_LoadedBuffers.back_ptr(); + m_LoadedBuffers.remove(*theBuffer); + } + NVScopedRefCounted<ILoadedBuffer> retval(theBuffer->m_Result); + if (retval.mPtr == NULL) { + retval = SBufferLoadResult::Allocate(0, m_Foundation, theBuffer->m_Path, + theBuffer->m_UserData); + } + NVDelete(m_Foundation.getAllocator(), theBuffer); + return retval; + } +}; +} + +IBufferLoader &IBufferLoader::Create(NVFoundationBase &fnd, IInputStreamFactory &inFactory, + IThreadPool &inThreadPool) +{ + return *QT3DS_NEW(fnd.getAllocator(), SBufferLoader)(fnd, inFactory, inThreadPool); +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h new file mode 100644 index 0000000..8d75e25 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_BUFFER_LOADED_H +#define QT3DS_RENDER_BUFFER_LOADED_H + +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace render { + + class IBufferLoaderCallback; + + class ILoadedBuffer : public NVRefCounted + { + public: + virtual CRegisteredString Path() = 0; + // Data is released when the buffer itself is released. + virtual NVDataRef<QT3DSU8> Data() = 0; + virtual IBufferLoaderCallback *UserData() = 0; + }; + + class IBufferLoaderCallback : public NVRefCounted + { + public: + virtual void OnBufferLoaded(ILoadedBuffer &inBuffer) = 0; + virtual void OnBufferLoadFailed(CRegisteredString inPath) = 0; + virtual void OnBufferLoadCancelled(CRegisteredString inPath) = 0; + }; + + // Job of this object is to load buffers all the way to memory as fast as possible. + class IBufferLoader : public NVRefCounted + { + public: + // nonblocking. Quiet failure is passed to the input stream factory. + // Returns handle to loading buffer + virtual QT3DSU64 QueueForLoading(CRegisteredString inPath, + IBufferLoaderCallback *inUserData = NULL, + bool inQuietFailure = false) = 0; + // Cancel a buffer that has not made it to the loaded buffers list. + virtual void CancelBufferLoad(QT3DSU64 inBufferId) = 0; + // If we were will to wait, will we ever get another buffer + virtual bool WillLoadedBuffersBeAvailable() = 0; + // Will nextLoadedBuffer block or not? + virtual bool AreLoadedBuffersAvailable() = 0; + + // blocking, be careful with this. No guarantees about timely return here. + virtual NVScopedRefCounted<ILoadedBuffer> NextLoadedBuffer() = 0; + + static IBufferLoader &Create(NVFoundationBase &fnd, IInputStreamFactory &inFactory, + IThreadPool &inThreadPool); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp new file mode 100644 index 0000000..eb23f3d --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp @@ -0,0 +1,1092 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#ifdef _WIN32 +#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union +#endif +#include "Qt3DSRender.h" +#include "Qt3DSRenderBufferManager.h" +#include "EASTL/string.h" +#include "foundation/Qt3DSAllocator.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSAtomic.h" +#include "EASTL/hash_map.h" +#include "foundation/FileTools.h" +#include "Qt3DSImportMesh.h" +#include "Qt3DSRenderMesh.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "Qt3DSRenderLoadedTexture.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "Qt3DSRenderImageScaler.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSTextRenderer.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "foundation/Qt3DSMutex.h" +#include "Qt3DSRenderPrefilterTexture.h" +#include <QtCore/qdir.h> + +using namespace qt3ds::render; + +namespace { + +using eastl::hash; +using eastl::pair; +using eastl::make_pair; +typedef eastl::basic_string<char8_t, ForwardingAllocator> TStr; +struct StrHasher +{ + size_t operator()(const TStr &str) const + { + return hash<const char8_t *>()((const char8_t *)str.c_str()); + } +}; + +struct StrEq +{ + bool operator()(const TStr &lhs, const TStr &rhs) const { return lhs == rhs; } +}; + +struct SImageEntry : public SImageTextureData +{ + bool m_Loaded; + SImageEntry() + : SImageTextureData(), m_Loaded(false) + { + } + SImageEntry(const SImageEntry &entry) + : SImageTextureData(entry), m_Loaded(entry.m_Loaded) + { + + } +}; + +struct SPrimitiveEntry +{ + // Name of the primitive as it will be in the UIP file + CRegisteredString m_PrimitiveName; + // Name of the primitive file on the filesystem + CRegisteredString m_FileName; +}; + +struct SBufferManager : public IBufferManager +{ + typedef eastl::hash_set<CRegisteredString, eastl::hash<CRegisteredString>, + eastl::equal_to<CRegisteredString>, ForwardingAllocator> + TStringSet; + typedef nvhash_map<CRegisteredString, SImageEntry> TImageMap; + typedef nvhash_map<CRegisteredString, SRenderMesh *> TMeshMap; + typedef nvhash_map<CRegisteredString, CRegisteredString> TAliasImageMap; + + NVScopedRefCounted<NVRenderContext> m_Context; + NVScopedRefCounted<IStringTable> m_StrTable; + NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory; + IPerfTimer &m_PerfTimer; + volatile QT3DSI32 mRefCount; + TStr m_PathBuilder; + TImageMap m_ImageMap; + Mutex m_LoadedImageSetMutex; + TStringSet m_LoadedImageSet; + TAliasImageMap m_AliasImageMap; + TMeshMap m_MeshMap; + SPrimitiveEntry m_PrimitiveNames[5]; + nvvector<qt3ds::render::NVRenderVertexBufferEntry> m_EntryBuffer; + bool m_GPUSupportsDXT; + bool m_reloadableResources; + + QHash<QString, ReloadableTexturePtr> m_reloadableTextures; + + static const char8_t *GetPrimitivesDirectory() { return "res//primitives"; } + + SBufferManager(NVRenderContext &ctx, IStringTable &strTable, + IInputStreamFactory &inInputStreamFactory, IPerfTimer &inTimer) + : m_Context(ctx) + , m_StrTable(strTable) + , m_InputStreamFactory(inInputStreamFactory) + , m_PerfTimer(inTimer) + , mRefCount(0) + , m_PathBuilder(ForwardingAllocator(ctx.GetAllocator(), "SBufferManager::m_PathBuilder")) + , m_ImageMap(ctx.GetAllocator(), "SBufferManager::m_ImageMap") + , m_LoadedImageSetMutex(ctx.GetAllocator()) + , m_LoadedImageSet( + ForwardingAllocator(ctx.GetAllocator(), "SBufferManager::m_LoadedImageSet")) + , m_AliasImageMap(ctx.GetAllocator(), "SBufferManager::m_AliasImageMap") + , m_MeshMap(ctx.GetAllocator(), "SBufferManager::m_MeshMap") + , m_EntryBuffer(ctx.GetAllocator(), "SBufferManager::m_EntryBuffer") + , m_GPUSupportsDXT(ctx.AreDXTImagesSupported()) + , m_reloadableResources(false) + { + } + virtual ~SBufferManager() { Clear(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Context->GetAllocator()) + + CRegisteredString CombineBaseAndRelative(const char8_t *inBase, + const char8_t *inRelative) override + { + CFileTools::CombineBaseAndRelative(inBase, inRelative, m_PathBuilder); + return m_StrTable->RegisterStr(m_PathBuilder.c_str()); + } + + void SetImageHasTransparency(CRegisteredString inImagePath, bool inHasTransparency) override + { + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(inImagePath, SImageEntry())); + theImage.first->second.m_TextureFlags.SetHasTransparency(inHasTransparency); + } + + bool GetImageHasTransparency(CRegisteredString inSourcePath) const override + { + TImageMap::const_iterator theIter = m_ImageMap.find(inSourcePath); + if (theIter != m_ImageMap.end()) + return theIter->second.m_TextureFlags.HasTransparency(); + return false; + } + + void SetImageTransparencyToFalseIfNotSet(CRegisteredString inSourcePath) override + { + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(inSourcePath, SImageEntry())); + // If we did actually insert something + if (theImage.second) + theImage.first->second.m_TextureFlags.SetHasTransparency(false); + } + + void SetInvertImageUVCoords(CRegisteredString inImagePath, bool inShouldInvertCoords) override + { + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(inImagePath, SImageEntry())); + theImage.first->second.m_TextureFlags.SetInvertUVCoords(inShouldInvertCoords); + } + + bool IsImageLoaded(CRegisteredString inSourcePath) override + { + Mutex::ScopedLock __locker(m_LoadedImageSetMutex); + return m_LoadedImageSet.find(inSourcePath) != m_LoadedImageSet.end(); + } + + bool AliasImagePath(CRegisteredString inSourcePath, CRegisteredString inAliasPath, + bool inIgnoreIfLoaded) override + { + if (inSourcePath.IsValid() == false || inAliasPath.IsValid() == false) + return false; + // If the image is loaded then we ignore this call in some cases. + if (inIgnoreIfLoaded && IsImageLoaded(inSourcePath)) + return false; + m_AliasImageMap.insert(eastl::make_pair(inSourcePath, inAliasPath)); + return true; + } + + void UnaliasImagePath(CRegisteredString inSourcePath) override + { + m_AliasImageMap.erase(inSourcePath); + } + + CRegisteredString GetImagePath(CRegisteredString inSourcePath) override + { + TAliasImageMap::iterator theAliasIter = m_AliasImageMap.find(inSourcePath); + if (theAliasIter != m_AliasImageMap.end()) + return theAliasIter->second; + return inSourcePath; + } + + CRegisteredString getImagePath(const QString &path) + { + TAliasImageMap::iterator theAliasIter + = m_AliasImageMap.find(m_StrTable->RegisterStr(qPrintable(path))); + if (theAliasIter != m_AliasImageMap.end()) + return theAliasIter->second; + return m_StrTable->RegisterStr(qPrintable(path)); + } + + static inline int wrapMod(int a, int base) + { + int ret = a % base; + if (ret < 0) + ret += base; + return ret; + } + + static inline void getWrappedCoords(int &sX, int &sY, int width, int height) + { + if (sY < 0) { + sX -= width >> 1; + sY = -sY; + } + if (sY >= height) { + sX += width >> 1; + sY = height - sY; + } + sX = wrapMod(sX, width); + sY = wrapMod(sY, height); + } + + template <typename V, typename C> + void iterateAll(const V &vv, C c) + { + for (const auto x : vv) + c(x); + } + + void loadTextureImage(SReloadableImageTextureData &data) + { + CRegisteredString imagePath = getImagePath(data.m_path); + TImageMap::iterator theIter = m_ImageMap.find(imagePath); + if ((theIter == m_ImageMap.end() || theIter->second.m_Loaded == false) + && imagePath.IsValid()) { + NVScopedReleasable<SLoadedTexture> theLoadedImage; + SImageTextureData textureData; + + doImageLoad(imagePath, theLoadedImage); + + if (theLoadedImage) { + textureData = LoadRenderImage(imagePath, *theLoadedImage, data.m_scanTransparency, + data.m_bsdfMipmap); + data.m_Texture = textureData.m_Texture; + data.m_TextureFlags = textureData.m_TextureFlags; + data.m_BSDFMipMap = textureData.m_BSDFMipMap; + data.m_loaded = true; + iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); }); + } else { + // We want to make sure that bad path fails once and doesn't fail over and over + // again which could slow down the system quite a bit. + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(imagePath, SImageEntry())); + theImage.first->second.m_Loaded = true; + qCWarning(WARNING, "Failed to load image: %s", imagePath.c_str()); + theIter = theImage.first; + } + } else { + SImageEntry textureData = theIter->second; + if (textureData.m_Loaded) { + data.m_Texture = textureData.m_Texture; + data.m_TextureFlags = textureData.m_TextureFlags; + data.m_BSDFMipMap = textureData.m_BSDFMipMap; + data.m_loaded = true; + iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); }); + } + } + } + + void unloadTextureImage(SReloadableImageTextureData &data) + { + CRegisteredString r = m_StrTable->RegisterStr(qPrintable(data.m_path)); + data.m_loaded = false; + data.m_Texture = nullptr; + data.m_BSDFMipMap = nullptr; + data.m_TextureFlags = {}; + iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); }); + InvalidateBuffer(r); + } + + void loadSet(const QSet<QString> &imageSet) override + { + for (const auto &x : imageSet) { + if (!m_reloadableTextures.contains(x)) { + auto img = CreateReloadableImage(m_StrTable->RegisterStr(qPrintable(x)), false, + false); + img->m_initialized = false; + loadTextureImage(*m_reloadableTextures[x]); + } else if (!m_reloadableTextures[x]->m_loaded) { + loadTextureImage(*m_reloadableTextures[x]); + } + } + } + + void unloadSet(const QSet<QString> &imageSet) override + { + for (const auto &x : imageSet) { + if (m_reloadableTextures.contains(x)) { + if (m_reloadableTextures[x]->m_loaded) + unloadTextureImage(*m_reloadableTextures[x]); + } + } + } + + virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath, + bool inForceScanForTransparency, + bool inBsdfMipmaps) override + { + QString path = QString::fromLatin1(inSourcePath.c_str()); + const bool inserted = m_reloadableTextures.contains(path); + if (!inserted || (inserted && m_reloadableTextures[path]->m_initialized == false)) { + if (!inserted) + m_reloadableTextures.insert(path, ReloadableTexturePtr::create()); + m_reloadableTextures[path]->m_path = path; + m_reloadableTextures[path]->m_scanTransparency = inForceScanForTransparency; + m_reloadableTextures[path]->m_bsdfMipmap = inBsdfMipmaps; + m_reloadableTextures[path]->m_initialized = true; + + if (!m_reloadableResources) + loadTextureImage(*m_reloadableTextures[path]); + + CRegisteredString imagePath = getImagePath(path); + TImageMap::iterator theIter = m_ImageMap.find(imagePath); + if (theIter != m_ImageMap.end()) { + SImageEntry textureData = theIter->second; + if (textureData.m_Loaded) { + m_reloadableTextures[path]->m_Texture = textureData.m_Texture; + m_reloadableTextures[path]->m_TextureFlags = textureData.m_TextureFlags; + m_reloadableTextures[path]->m_BSDFMipMap = textureData.m_BSDFMipMap; + m_reloadableTextures[path]->m_loaded = true; + } + } + } + return m_reloadableTextures[path]; + } + + void doImageLoad(CRegisteredString inImagePath, + NVScopedReleasable<SLoadedTexture> &theLoadedImage) + { + SStackPerfTimer __perfTimer(m_PerfTimer, "Image Decompression"); + theLoadedImage = SLoadedTexture::Load( + inImagePath.c_str(), m_Context->GetFoundation(), *m_InputStreamFactory, + true, m_Context->GetRenderContextType()); + // Hackish solution to custom materials not finding their textures if they are used + // in sub-presentations. + if (!theLoadedImage) { + if (QDir(inImagePath.c_str()).isRelative()) { + QString searchPath = inImagePath.c_str(); + if (searchPath.startsWith(QLatin1String("./"))) + searchPath.prepend(QLatin1Char('.')); + int loops = 0; + while (!theLoadedImage && ++loops <= 3) { + theLoadedImage = SLoadedTexture::Load( + searchPath.toUtf8(), m_Context->GetFoundation(), + *m_InputStreamFactory, true, + m_Context->GetRenderContextType()); + searchPath.prepend(QLatin1String("../")); + } + } else { + // Some textures, for example environment maps for custom materials, + // have absolute path at this point. It points to the wrong place with + // the new project structure, so we need to split it up and construct + // the new absolute path here. + QString wholePath = inImagePath.c_str(); + QStringList splitPath = wholePath.split(QLatin1String("../")); + if (splitPath.size() > 1) { + QString searchPath = splitPath.at(0) + splitPath.at(1); + int loops = 0; + while (!theLoadedImage && ++loops <= 3) { + theLoadedImage = SLoadedTexture::Load( + searchPath.toUtf8(), m_Context->GetFoundation(), + *m_InputStreamFactory, true, + m_Context->GetRenderContextType()); + searchPath = splitPath.at(0); + for (int i = 0; i < loops; i++) + searchPath.append(QLatin1String("../")); + searchPath.append(splitPath.at(1)); + } + } + } + } + } + + void enableReloadableResources(bool enable) override + { + m_reloadableResources = enable; + } + + bool isReloadableResourcesEnabled() const override + { + return m_reloadableResources; + } + + SImageTextureData LoadRenderImage(CRegisteredString inImagePath, + SLoadedTexture &inLoadedImage, + bool inForceScanForTransparency, bool inBsdfMipmaps) override + { + SStackPerfTimer __perfTimer(m_PerfTimer, "Image Upload"); + { + Mutex::ScopedLock __mapLocker(m_LoadedImageSetMutex); + m_LoadedImageSet.insert(inImagePath); + } + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(inImagePath, SImageEntry())); + bool wasInserted = theImage.second; + theImage.first->second.m_Loaded = true; + // inLoadedImage.EnsureMultiplerOfFour( m_Context->GetFoundation(), inImagePath.c_str() ); + + NVRenderTexture2D *theTexture = m_Context->CreateTexture2D(); + if (inLoadedImage.data) { + qt3ds::render::NVRenderTextureFormats::Enum destFormat = inLoadedImage.format; + if (inBsdfMipmaps) { + if (m_Context->GetRenderContextType() == render::NVRenderContextValues::GLES2) + destFormat = qt3ds::render::NVRenderTextureFormats::RGBA8; + else + destFormat = qt3ds::render::NVRenderTextureFormats::RGBA16F; + } + else { + theTexture->SetTextureData( + NVDataRef<QT3DSU8>((QT3DSU8 *)inLoadedImage.data, inLoadedImage.dataSizeInBytes), 0, + inLoadedImage.width, inLoadedImage.height, inLoadedImage.format, destFormat); + { + static int enable = qEnvironmentVariableIntValue("QT3DS_GENERATE_MIPMAPS"); + if (enable) { + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::LinearMipmapLinear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + theTexture->GenerateMipmaps(); + } + } + } + + if (inBsdfMipmaps + && NVRenderTextureFormats::isUncompressedTextureFormat(inLoadedImage.format)) { + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::LinearMipmapLinear); + Qt3DSRenderPrefilterTexture *theBSDFMipMap = theImage.first->second.m_BSDFMipMap; + if (theBSDFMipMap == NULL) { + theBSDFMipMap = Qt3DSRenderPrefilterTexture::Create( + m_Context, inLoadedImage.width, inLoadedImage.height, *theTexture, + destFormat, m_Context->GetFoundation()); + theImage.first->second.m_BSDFMipMap = theBSDFMipMap; + } + + if (theBSDFMipMap) { + theBSDFMipMap->Build(inLoadedImage.data, inLoadedImage.dataSizeInBytes, + inLoadedImage.format); + } + } + } else if (inLoadedImage.dds) { + theImage.first->second.m_Texture = theTexture; + bool supportsDXT = m_GPUSupportsDXT; + bool isDXT = NVRenderTextureFormats::isCompressedTextureFormat(inLoadedImage.format); + bool requiresDecompression = (supportsDXT == false && isDXT) || false; + // test code for DXT decompression + // if ( isDXT ) requiresDecompression = true; + if (requiresDecompression) { + qCWarning(WARNING, PERF_INFO, + "Image %s is DXT format which is unsupported by " + "the graphics subsystem, decompressing in CPU", + inImagePath.c_str()); + } + STextureData theDecompressedImage; + for (int idx = 0; idx < inLoadedImage.dds->numMipmaps; ++idx) { + if (inLoadedImage.dds->mipwidth[idx] && inLoadedImage.dds->mipheight[idx]) { + if (requiresDecompression == false) { + theTexture->SetTextureData( + toU8DataRef((char *)inLoadedImage.dds->data[idx], + (QT3DSU32)inLoadedImage.dds->size[idx]), + (QT3DSU8)idx, (QT3DSU32)inLoadedImage.dds->mipwidth[idx], + (QT3DSU32)inLoadedImage.dds->mipheight[idx], inLoadedImage.format); + } else { + theDecompressedImage = + inLoadedImage.DecompressDXTImage(idx, &theDecompressedImage); + + if (theDecompressedImage.data) { + theTexture->SetTextureData( + toU8DataRef((char *)theDecompressedImage.data, + (QT3DSU32)theDecompressedImage.dataSizeInBytes), + (QT3DSU8)idx, (QT3DSU32)inLoadedImage.dds->mipwidth[idx], + (QT3DSU32)inLoadedImage.dds->mipheight[idx], + theDecompressedImage.format); + } + } + } + } + if (theDecompressedImage.data) + inLoadedImage.ReleaseDecompressedTexture(theDecompressedImage); + } + if (wasInserted == true || inForceScanForTransparency) + theImage.first->second.m_TextureFlags.SetHasTransparency( + inLoadedImage.ScanForTransparency()); + theImage.first->second.m_Texture = theTexture; + return theImage.first->second; + } + + SImageTextureData LoadRenderImage(CRegisteredString inImagePath, + bool inForceScanForTransparency, bool inBsdfMipmaps) override + { + inImagePath = GetImagePath(inImagePath); + + if (!inImagePath.IsValid()) + return SImageEntry(); + + TImageMap::iterator theIter = m_ImageMap.find(inImagePath); + if (theIter == m_ImageMap.end() && inImagePath.IsValid()) { + NVScopedReleasable<SLoadedTexture> theLoadedImage; + + doImageLoad(inImagePath, theLoadedImage); + + if (theLoadedImage) { + return LoadRenderImage(inImagePath, *theLoadedImage, inForceScanForTransparency, + inBsdfMipmaps); + } else { + // We want to make sure that bad path fails once and doesn't fail over and over + // again + // which could slow down the system quite a bit. + pair<TImageMap::iterator, bool> theImage = + m_ImageMap.insert(make_pair(inImagePath, SImageEntry())); + theImage.first->second.m_Loaded = true; + qCWarning(WARNING, "Failed to load image: %s", inImagePath.c_str()); + theIter = theImage.first; + } + } + return theIter->second; + } + + qt3dsimp::SMultiLoadResult LoadPrimitive(const char8_t *inRelativePath) + { + CRegisteredString theName(m_StrTable->RegisterStr(inRelativePath)); + if (m_PrimitiveNames[0].m_PrimitiveName.IsValid() == false) { + IStringTable &strTable(m_Context->GetStringTable()); + m_PrimitiveNames[0].m_PrimitiveName = strTable.RegisterStr("#Rectangle"); + m_PrimitiveNames[0].m_FileName = strTable.RegisterStr("Rectangle.mesh"); + m_PrimitiveNames[1].m_PrimitiveName = strTable.RegisterStr("#Sphere"); + m_PrimitiveNames[1].m_FileName = strTable.RegisterStr("Sphere.mesh"); + m_PrimitiveNames[2].m_PrimitiveName = strTable.RegisterStr("#Cube"); + m_PrimitiveNames[2].m_FileName = strTable.RegisterStr("Cube.mesh"); + m_PrimitiveNames[3].m_PrimitiveName = strTable.RegisterStr("#Cone"); + m_PrimitiveNames[3].m_FileName = strTable.RegisterStr("Cone.mesh"); + m_PrimitiveNames[4].m_PrimitiveName = strTable.RegisterStr("#Cylinder"); + m_PrimitiveNames[4].m_FileName = strTable.RegisterStr("Cylinder.mesh"); + } + for (size_t idx = 0; idx < 5; ++idx) { + if (m_PrimitiveNames[idx].m_PrimitiveName == theName) { + CFileTools::CombineBaseAndRelative(GetPrimitivesDirectory(), + m_PrimitiveNames[idx].m_FileName, m_PathBuilder); + QT3DSU32 id = 1; + NVScopedRefCounted<IRefCountedInputStream> theInStream( + m_InputStreamFactory->GetStreamForFile(m_PathBuilder.c_str())); + if (theInStream) + return qt3dsimp::Mesh::LoadMulti(m_Context->GetAllocator(), *theInStream, id); + else { + qCCritical(INTERNAL_ERROR, "Unable to find mesh primitive %s", + m_PathBuilder.c_str()); + return qt3dsimp::SMultiLoadResult(); + } + } + } + return qt3dsimp::SMultiLoadResult(); + } + + virtual NVConstDataRef<QT3DSU8> CreatePackedPositionDataArray( + const qt3dsimp::SMultiLoadResult &inResult) + { + // we assume a position consists of 3 floats + QT3DSU32 vertexCount = inResult.m_Mesh->m_VertexBuffer.m_Data.size() + / inResult.m_Mesh->m_VertexBuffer.m_Stride; + QT3DSU32 dataSize = vertexCount * 3 * sizeof(QT3DSF32); + QT3DSF32 *posData = static_cast<QT3DSF32 *>( + QT3DS_ALLOC(m_Context->GetAllocator(), dataSize, + "SRenderMesh::CreatePackedPositionDataArray")); + QT3DSU8 *baseOffset = reinterpret_cast<QT3DSU8 *>(inResult.m_Mesh); + // copy position data + if (posData) { + QT3DSF32 *srcData = reinterpret_cast<QT3DSF32 *>( + inResult.m_Mesh->m_VertexBuffer.m_Data.begin(baseOffset)); + QT3DSU32 srcStride = inResult.m_Mesh->m_VertexBuffer.m_Stride / sizeof(QT3DSF32); + QT3DSF32 *dstData = posData; + QT3DSU32 dstStride = 3; + + for (QT3DSU32 i = 0; i < vertexCount; ++i) { + dstData[0] = srcData[0]; + dstData[1] = srcData[1]; + dstData[2] = srcData[2]; + + dstData += dstStride; + srcData += srcStride; + } + + return toConstDataRef(reinterpret_cast<const qt3ds::QT3DSU8 *>(posData), dataSize); + } + + return NVConstDataRef<QT3DSU8>(); + } + + SRenderMesh *createRenderMesh(const qt3dsimp::SMultiLoadResult &result) + { + SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)( + qt3ds::render::NVRenderDrawMode::Triangles, + qt3ds::render::NVRenderWinding::CounterClockwise, result.m_Id, + m_Context->GetAllocator()); + QT3DSU8 *baseAddress = reinterpret_cast<QT3DSU8 *>(result.m_Mesh); + NVConstDataRef<QT3DSU8> theVBufData( + result.m_Mesh->m_VertexBuffer.m_Data.begin(baseAddress), + result.m_Mesh->m_VertexBuffer.m_Data.size()); + + NVRenderVertexBuffer *theVertexBuffer = m_Context->CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, + result.m_Mesh->m_VertexBuffer.m_Data.m_Size, + result.m_Mesh->m_VertexBuffer.m_Stride, theVBufData); + + // create a tight packed position data VBO + // this should improve our depth pre pass rendering + NVRenderVertexBuffer *thePosVertexBuffer = nullptr; + NVConstDataRef<QT3DSU8> posData = CreatePackedPositionDataArray(result); + if (posData.size()) { + thePosVertexBuffer + = m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static, + posData.size(), 3 * sizeof(QT3DSF32), posData); + } + + NVRenderIndexBuffer *theIndexBuffer = nullptr; + if (result.m_Mesh->m_IndexBuffer.m_Data.size()) { + using qt3ds::render::NVRenderComponentTypes; + QT3DSU32 theIndexBufferSize = result.m_Mesh->m_IndexBuffer.m_Data.size(); + NVRenderComponentTypes::Enum bufComponentType = + result.m_Mesh->m_IndexBuffer.m_ComponentType; + QT3DSU32 sizeofType + = qt3ds::render::NVRenderComponentTypes::getSizeofType(bufComponentType); + + if (sizeofType == 2 || sizeofType == 4) { + // Ensure type is unsigned; else things will fail in rendering pipeline. + if (bufComponentType == NVRenderComponentTypes::QT3DSI16) + bufComponentType = NVRenderComponentTypes::QT3DSU16; + if (bufComponentType == NVRenderComponentTypes::QT3DSI32) + bufComponentType = NVRenderComponentTypes::QT3DSU32; + + NVConstDataRef<QT3DSU8> theIBufData( + result.m_Mesh->m_IndexBuffer.m_Data.begin(baseAddress), + result.m_Mesh->m_IndexBuffer.m_Data.size()); + theIndexBuffer = m_Context->CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, bufComponentType, + theIndexBufferSize, theIBufData); + } else { + QT3DS_ASSERT(false); + } + } + nvvector<qt3ds::render::NVRenderVertexBufferEntry> &theEntryBuffer(m_EntryBuffer); + theEntryBuffer.resize(result.m_Mesh->m_VertexBuffer.m_Entries.size()); + for (QT3DSU32 entryIdx = 0, + entryEnd = result.m_Mesh->m_VertexBuffer.m_Entries.size(); + entryIdx < entryEnd; ++entryIdx) { + theEntryBuffer[entryIdx] + = result.m_Mesh->m_VertexBuffer.m_Entries.index(baseAddress, entryIdx) + .ToVertexBufferEntry(baseAddress); + } + // create our attribute layout + NVRenderAttribLayout *theAttribLayout + = m_Context->CreateAttributeLayout(theEntryBuffer); + // create our attribute layout for depth pass + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + }; + NVRenderAttribLayout *theAttribLayoutDepth + = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1)); + + // create input assembler object + QT3DSU32 strides = result.m_Mesh->m_VertexBuffer.m_Stride; + QT3DSU32 offsets = 0; + NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler( + theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1), + result.m_Mesh->m_DrawMode); + + // create depth input assembler object + QT3DSU32 posStrides = thePosVertexBuffer ? 3 * sizeof(QT3DSF32) : strides; + NVRenderInputAssembler *theInputAssemblerDepth = m_Context->CreateInputAssembler( + theAttribLayoutDepth, + toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1), + theIndexBuffer, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1), + result.m_Mesh->m_DrawMode); + + NVRenderInputAssembler *theInputAssemblerPoints = m_Context->CreateInputAssembler( + theAttribLayoutDepth, + toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1), + nullptr, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1), + NVRenderDrawMode::Points); + + if (!theInputAssembler || !theInputAssemblerDepth || !theInputAssemblerPoints) { + QT3DS_ASSERT(false); + return nullptr; + } + theNewMesh->m_Joints.resize(result.m_Mesh->m_Joints.size()); + for (QT3DSU32 jointIdx = 0, jointEnd = result.m_Mesh->m_Joints.size(); + jointIdx < jointEnd; ++jointIdx) { + const qt3dsimp::Joint &theImportJoint( + result.m_Mesh->m_Joints.index(baseAddress, jointIdx)); + SRenderJoint &theNewJoint(theNewMesh->m_Joints[jointIdx]); + theNewJoint.m_JointID = theImportJoint.m_JointID; + theNewJoint.m_ParentID = theImportJoint.m_ParentID; + memCopy(theNewJoint.m_invBindPose, theImportJoint.m_invBindPose, + 16 * sizeof(QT3DSF32)); + memCopy(theNewJoint.m_localToGlobalBoneSpace, + theImportJoint.m_localToGlobalBoneSpace, 16 * sizeof(QT3DSF32)); + } + + for (QT3DSU32 subsetIdx = 0, subsetEnd = result.m_Mesh->m_Subsets.size(); + subsetIdx < subsetEnd; ++subsetIdx) { + SRenderSubset theSubset(m_Context->GetAllocator()); + const qt3dsimp::MeshSubset &source( + result.m_Mesh->m_Subsets.index(baseAddress, subsetIdx)); + theSubset.m_Bounds = source.m_Bounds; + theSubset.m_Count = source.m_Count; + theSubset.m_Offset = source.m_Offset; + theSubset.m_Joints = theNewMesh->m_Joints; + theSubset.m_Name = m_StrTable->RegisterStr(source.m_Name.begin(baseAddress)); + theVertexBuffer->addRef(); + theSubset.m_VertexBuffer = theVertexBuffer; + if (thePosVertexBuffer) { + thePosVertexBuffer->addRef(); + theSubset.m_PosVertexBuffer = thePosVertexBuffer; + } + if (theIndexBuffer) { + theIndexBuffer->addRef(); + theSubset.m_IndexBuffer = theIndexBuffer; + } + theSubset.m_InputAssembler = theInputAssembler; + theSubset.m_InputAssemblerDepth = theInputAssemblerDepth; + theSubset.m_InputAssemblerPoints = theInputAssemblerPoints; + theSubset.m_PrimitiveType = result.m_Mesh->m_DrawMode; + theInputAssembler->addRef(); + theInputAssemblerDepth->addRef(); + theSubset.m_InputAssemblerPoints->addRef(); + theNewMesh->m_Subsets.push_back(theSubset); + } + // If we want to, we can break up models into sub-subsets. + // These are assumed to use the same material as the outer subset but have fewer triangles + // and should have a more exact bounding box. This sort of thing helps with using the + // frustum culling system but it is really done incorrectly. + // It should be done via some sort of oct-tree mechanism and so that the sub-subsets + // are spatially sorted and it should only be done upon save-to-binary with the + // results saved out to disk. As you can see, doing it properly requires some real + // engineering effort so it is somewhat unlikely it will ever happen. + // Or it could be done on import if someone really wants to change the mesh buffer + // format. Either way it isn't going to happen here and it isn't going to happen this way + // but this is a working example of using the technique. +#ifdef QT3DS_RENDER_GENERATE_SUB_SUBSETS + Option<qt3ds::render::NVRenderVertexBufferEntry> thePosAttrOpt + = theVertexBuffer->GetEntryByName("attr_pos"); + bool hasPosAttr = thePosAttrOpt.hasValue() + && thePosAttrOpt->m_ComponentType == qt3ds::render::NVRenderComponentTypes::QT3DSF32 + && thePosAttrOpt->m_NumComponents == 3; + + for (size_t subsetIdx = 0, subsetEnd = theNewMesh->m_Subsets.size(); + subsetIdx < subsetEnd; ++subsetIdx) { + SRenderSubset &theOuterSubset = theNewMesh->m_Subsets[subsetIdx]; + if (theOuterSubset.m_Count && theIndexBuffer + && theIndexBuffer->GetComponentType() + == qt3ds::render::NVRenderComponentTypes::QT3DSU16 + && theNewMesh->m_DrawMode == NVRenderDrawMode::Triangles && hasPosAttr) { + // Num tris in a sub subset. + QT3DSU32 theSubsetSize = 3334 * 3; // divisible by three. + size_t theNumSubSubsets = ((theOuterSubset.m_Count - 1) / theSubsetSize) + 1; + QT3DSU32 thePosAttrOffset = thePosAttrOpt->m_FirstItemOffset; + const QT3DSU8 *theVertData = result.m_Mesh->m_VertexBuffer.m_Data.begin(); + const QT3DSU8 *theIdxData = result.m_Mesh->m_IndexBuffer.m_Data.begin(); + QT3DSU32 theVertStride = result.m_Mesh->m_VertexBuffer.m_Stride; + QT3DSU32 theOffset = theOuterSubset.m_Offset; + QT3DSU32 theCount = theOuterSubset.m_Count; + for (size_t subSubsetIdx = 0, subSubsetEnd = theNumSubSubsets; + subSubsetIdx < subSubsetEnd; ++subSubsetIdx) { + SRenderSubsetBase theBase; + theBase.m_Offset = theOffset; + theBase.m_Count = NVMin(theSubsetSize, theCount); + theBase.m_Bounds.setEmpty(); + theCount -= theBase.m_Count; + theOffset += theBase.m_Count; + // Create new bounds. + // Offset is in item size, not bytes. + const QT3DSU16 *theSubsetIdxData + = reinterpret_cast<const QT3DSU16 *>(theIdxData + theBase.m_Offset * 2); + for (size_t theIdxIdx = 0, theIdxEnd = theBase.m_Count; + theIdxIdx < theIdxEnd; ++theIdxIdx) { + QT3DSU32 theVertOffset = theSubsetIdxData[theIdxIdx] * theVertStride; + theVertOffset += thePosAttrOffset; + QT3DSVec3 thePos = *( + reinterpret_cast<const QT3DSVec3 *>(theVertData + theVertOffset)); + theBase.m_Bounds.include(thePos); + } + theOuterSubset.m_SubSubsets.push_back(theBase); + } + } else { + SRenderSubsetBase theBase; + theBase.m_Bounds = theOuterSubset.m_Bounds; + theBase.m_Count = theOuterSubset.m_Count; + theBase.m_Offset = theOuterSubset.m_Offset; + theOuterSubset.m_SubSubsets.push_back(theBase); + } + } +#endif + if (posData.size()) { + m_Context->GetAllocator().deallocate( + static_cast<void *>(const_cast<qt3ds::QT3DSU8 *>(posData.begin()))); + } + + return theNewMesh; + } + + void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) override + { + if (!name.isEmpty() && mesh) { + CRegisteredString meshName = m_StrTable->RegisterStr(name); + pair<TMeshMap::iterator, bool> theMesh + = m_MeshMap.insert({ meshName, static_cast<SRenderMesh *>(nullptr) }); + // Only create the mesh if it doesn't yet exist + if (theMesh.second) { + qt3dsimp::SMultiLoadResult result; + result.m_Mesh = mesh; + theMesh.first->second = createRenderMesh(result); + } + } + } + + SRenderMesh *LoadMesh(CRegisteredString inMeshPath) override + { + if (inMeshPath.IsValid() == false) + return nullptr; + pair<TMeshMap::iterator, bool> theMesh = + m_MeshMap.insert(make_pair(inMeshPath, static_cast<SRenderMesh *>(nullptr))); + if (theMesh.second) { + // Check to see if this is primitive + qt3dsimp::SMultiLoadResult theResult = LoadPrimitive(inMeshPath); + + // Attempt a load from the filesystem if this mesh isn't a primitive. + if (!theResult.m_Mesh) { + m_PathBuilder = inMeshPath; + TStr::size_type pound = m_PathBuilder.rfind('#'); + QT3DSU32 id = 0; + if (pound != TStr::npos) { + id = QT3DSU32(atoi(m_PathBuilder.c_str() + pound + 1)); + m_PathBuilder.erase(m_PathBuilder.begin() + pound, m_PathBuilder.end()); + } + NVScopedRefCounted<IRefCountedInputStream> theStream( + m_InputStreamFactory->GetStreamForFile(m_PathBuilder.c_str())); + if (theStream) { + theResult = qt3dsimp::Mesh::LoadMulti( + m_Context->GetAllocator(), *theStream, id); + } + if (!theResult.m_Mesh) + qCWarning(WARNING, "Failed to load mesh: %s", m_PathBuilder.c_str()); + } + + if (theResult.m_Mesh) { + theMesh.first->second = createRenderMesh(theResult); + m_Context->GetAllocator().deallocate(theResult.m_Mesh); + } + } + return theMesh.first->second; + } + + SRenderMesh *CreateMesh(Qt3DSBCharPtr inSourcePath, QT3DSU8 *inVertData, QT3DSU32 inNumVerts, + QT3DSU32 inVertStride, QT3DSU32 *inIndexData, QT3DSU32 inIndexCount, + qt3ds::NVBounds3 inBounds) override + { + CRegisteredString sourcePath = m_StrTable->RegisterStr(inSourcePath); + + // eastl::pair<CRegisteredString, SRenderMesh*> thePair(sourcePath, (SRenderMesh*)NULL); + pair<TMeshMap::iterator, bool> theMesh; + // Make sure there isn't already a buffer entry for this mesh. + if (m_MeshMap.contains(sourcePath)) { + theMesh = make_pair<TMeshMap::iterator, bool>(m_MeshMap.find(sourcePath), true); + } else { + theMesh = m_MeshMap.insert(make_pair(sourcePath, (SRenderMesh *)NULL)); + } + + if (theMesh.second == true) { + SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)( + qt3ds::render::NVRenderDrawMode::Triangles, + qt3ds::render::NVRenderWinding::CounterClockwise, 0, m_Context->GetAllocator()); + + // If we failed to create the RenderMesh, return a failure. + if (!theNewMesh) { + QT3DS_ASSERT(false); + return NULL; + } + + // Get rid of any old mesh that was sitting here and fill it with a new one. + // NOTE : This is assuming that the source of our mesh data doesn't do its own memory + // management and always returns new buffer pointers every time. + // Don't know for sure if that's what we'll get from our intended sources, but that's + // easily + // adjustable by looking for matching pointers in the Subsets. + if (theNewMesh && theMesh.first->second != NULL) { + delete theMesh.first->second; + theMesh.first->second = NULL; + } + + theMesh.first->second = theNewMesh; + QT3DSU32 vertDataSize = inNumVerts * inVertStride; + NVConstDataRef<QT3DSU8> theVBufData(inVertData, vertDataSize); + // NVConstDataRef<QT3DSU8> theVBufData( theResult.m_Mesh->m_VertexBuffer.m_Data.begin( + // baseAddress ) + // , theResult.m_Mesh->m_VertexBuffer.m_Data.size() ); + + NVRenderVertexBuffer *theVertexBuffer = + m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static, + vertDataSize, inVertStride, theVBufData); + NVRenderIndexBuffer *theIndexBuffer = NULL; + if (inIndexData != NULL && inIndexCount > 3) { + NVConstDataRef<QT3DSU8> theIBufData((QT3DSU8 *)inIndexData, inIndexCount * sizeof(QT3DSU32)); + theIndexBuffer = + m_Context->CreateIndexBuffer(qt3ds::render::NVRenderBufferUsageType::Static, + qt3ds::render::NVRenderComponentTypes::QT3DSU32, + inIndexCount * sizeof(QT3DSU32), theIBufData); + } + + // WARNING + // Making an assumption here about the contents of the stream + // PKC TODO : We may have to consider some other format. + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_uv", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_norm", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 18), + }; + + // create our attribute layout + NVRenderAttribLayout *theAttribLayout = + m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 3)); + /* + // create our attribute layout for depth pass + qt3ds::render::NVRenderVertexBufferEntry theEntriesDepth[] = { + qt3ds::render::NVRenderVertexBufferEntry( "attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3 ), + }; + NVRenderAttribLayout* theAttribLayoutDepth = m_Context->CreateAttributeLayout( + toConstDataRef( theEntriesDepth, 1 ) ); + */ + // create input assembler object + QT3DSU32 strides = inVertStride; + QT3DSU32 offsets = 0; + NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler( + theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1), + qt3ds::render::NVRenderDrawMode::Triangles); + + if (!theInputAssembler) { + QT3DS_ASSERT(false); + return NULL; + } + + // Pull out just the mesh object name from the total path + eastl::string fullName(inSourcePath); + eastl::string subName(inSourcePath); + if (fullName.rfind("#") != eastl::string::npos) { + subName = fullName.substr(fullName.rfind("#"), eastl::string::npos); + } + + theNewMesh->m_Joints.clear(); + SRenderSubset theSubset(m_Context->GetAllocator()); + theSubset.m_Bounds = inBounds; + theSubset.m_Count = inIndexCount; + theSubset.m_Offset = 0; + theSubset.m_Joints = theNewMesh->m_Joints; + theSubset.m_Name = m_StrTable->RegisterStr(subName.c_str()); + theVertexBuffer->addRef(); + theSubset.m_VertexBuffer = theVertexBuffer; + theSubset.m_PosVertexBuffer = NULL; + if (theIndexBuffer) + theIndexBuffer->addRef(); + theSubset.m_IndexBuffer = theIndexBuffer; + theSubset.m_InputAssembler = theInputAssembler; + theSubset.m_InputAssemblerDepth = theInputAssembler; + theSubset.m_InputAssemblerPoints = theInputAssembler; + theSubset.m_PrimitiveType = qt3ds::render::NVRenderDrawMode::Triangles; + theSubset.m_InputAssembler->addRef(); + theSubset.m_InputAssemblerDepth->addRef(); + theSubset.m_InputAssemblerPoints->addRef(); + theNewMesh->m_Subsets.push_back(theSubset); + } + + return theMesh.first->second; + } + + void ReleaseMesh(SRenderMesh &inMesh) + { + for (QT3DSU32 subsetIdx = 0, subsetEnd = inMesh.m_Subsets.size(); subsetIdx < subsetEnd; + ++subsetIdx) { + inMesh.m_Subsets[subsetIdx].m_VertexBuffer->release(); + if (inMesh.m_Subsets[subsetIdx].m_PosVertexBuffer) // can be NULL + inMesh.m_Subsets[subsetIdx].m_PosVertexBuffer->release(); + if (inMesh.m_Subsets[subsetIdx].m_IndexBuffer) // can be NULL + inMesh.m_Subsets[subsetIdx].m_IndexBuffer->release(); + inMesh.m_Subsets[subsetIdx].m_InputAssembler->release(); + inMesh.m_Subsets[subsetIdx].m_InputAssemblerDepth->release(); + if (inMesh.m_Subsets[subsetIdx].m_InputAssemblerPoints) + inMesh.m_Subsets[subsetIdx].m_InputAssemblerPoints->release(); + } + NVDelete(m_Context->GetAllocator(), &inMesh); + } + void ReleaseTexture(SImageEntry &inEntry) + { + if (inEntry.m_Texture) + inEntry.m_Texture->release(); + if (inEntry.m_BSDFMipMap) + inEntry.m_BSDFMipMap->release(); + } + void Clear() override + { + m_reloadableTextures.clear(); + for (TMeshMap::iterator iter = m_MeshMap.begin(), end = m_MeshMap.end(); iter != end; + ++iter) { + SRenderMesh *theMesh = iter->second; + if (theMesh) + ReleaseMesh(*theMesh); + } + m_MeshMap.clear(); + for (TImageMap::iterator iter = m_ImageMap.begin(), end = m_ImageMap.end(); iter != end; + ++iter) { + SImageEntry &theEntry = iter->second; + ReleaseTexture(theEntry); + } + m_ImageMap.clear(); + m_AliasImageMap.clear(); + { + Mutex::ScopedLock __locker(m_LoadedImageSetMutex); + m_LoadedImageSet.clear(); + } + } + void InvalidateBuffer(CRegisteredString inSourcePath) override + { + { + TMeshMap::iterator iter = m_MeshMap.find(inSourcePath); + if (iter != m_MeshMap.end()) { + if (iter->second) + ReleaseMesh(*iter->second); + m_MeshMap.erase(iter); + return; + } + } + { + TImageMap::iterator iter = m_ImageMap.find(inSourcePath); + if (iter != m_ImageMap.end()) { + SImageEntry &theEntry = iter->second; + ReleaseTexture(theEntry); + m_ImageMap.erase(inSourcePath); + { + Mutex::ScopedLock __locker(m_LoadedImageSetMutex); + m_LoadedImageSet.erase(inSourcePath); + } + } + } + } + IStringTable &GetStringTable() override { return *m_StrTable; } +}; +} + +IBufferManager &IBufferManager::Create(NVRenderContext &inRenderContext, IStringTable &inStrTable, + IInputStreamFactory &inFactory, IPerfTimer &inPerfTimer) +{ + return *QT3DS_NEW(inRenderContext.GetAllocator(), SBufferManager)(inRenderContext, inStrTable, + inFactory, inPerfTimer); +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h new file mode 100644 index 0000000..070ab67 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_BUFFER_MANAGER_H +#define QT3DS_RENDER_BUFFER_MANAGER_H +#include "Qt3DSRender.h" +#include "EASTL/utility.h" //pair +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderImageTextureData.h" +#include "foundation/Qt3DSBounds3.h" + +namespace qt3dsimp { + struct Mesh; +} + +namespace qt3ds { +namespace render { + + class IBufferManager : public NVRefCounted + { + protected: + virtual ~IBufferManager() {} + + public: + // Path manipulation used to get the final path form a base path plus relative extension + virtual CRegisteredString CombineBaseAndRelative(const char8_t *inBase, + const char8_t *inRelative) = 0; + virtual void SetImageHasTransparency(CRegisteredString inSourcePath, + bool inHasTransparency) = 0; + virtual bool GetImageHasTransparency(CRegisteredString inSourcePath) const = 0; + virtual void SetImageTransparencyToFalseIfNotSet(CRegisteredString inSourcePath) = 0; + virtual void SetInvertImageUVCoords(CRegisteredString inSourcePath, + bool inShouldInvertCoords) = 0; + + // Returns true if this image has been loaded into memory + // This call is threadsafe. Nothing else on this object is guaranteed to be. + virtual bool IsImageLoaded(CRegisteredString inSourcePath) = 0; + + // Alias one image path with another image path. Optionally this object will ignore the + // call if + // the source path is already loaded. Aliasing is currently used to allow a default image + // to be shown + // in place of an image that is loading offline. + // Returns true if the image was aliased, false otherwise. + virtual bool AliasImagePath(CRegisteredString inSourcePath, CRegisteredString inAliasPath, + bool inIgnoreIfLoaded) = 0; + virtual void UnaliasImagePath(CRegisteredString inSourcePath) = 0; + + // Returns the given source path unless the source path is aliased; in which case returns + // the aliased path. + virtual CRegisteredString GetImagePath(CRegisteredString inSourcePath) = 0; + // Returns a texture and a boolean indicating if this texture has transparency in it or not. + // Can't name this LoadImage because that gets mangled by windows to LoadImageA (uggh) + // In some cases we need to only scan particular images for transparency. + virtual SImageTextureData LoadRenderImage(CRegisteredString inImagePath, + SLoadedTexture &inTexture, + bool inForceScanForTransparency = false, + bool inBsdfMipmaps = false) = 0; + virtual SImageTextureData LoadRenderImage(CRegisteredString inSourcePath, + bool inForceScanForTransparency = false, + bool inBsdfMipmaps = false) = 0; + + virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath, + bool inForceScanForTransparency = false, + bool inBsdfMipmaps = false) = 0; + virtual void enableReloadableResources(bool enable) = 0; + virtual bool isReloadableResourcesEnabled() const = 0; + + virtual void loadSet(const QSet<QString> &imageSet) = 0; + virtual void unloadSet(const QSet<QString> &imageSet) = 0; + + virtual void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) = 0; + virtual SRenderMesh *LoadMesh(CRegisteredString inSourcePath) = 0; + + virtual SRenderMesh *CreateMesh(const char *inSourcePath, QT3DSU8 *inVertData, + QT3DSU32 inNumVerts, QT3DSU32 inVertStride, QT3DSU32 *inIndexData, + QT3DSU32 inIndexCount, qt3ds::NVBounds3 inBounds) = 0; + + // Remove *all* buffers from the buffer manager; + virtual void Clear() = 0; + virtual void InvalidateBuffer(CRegisteredString inSourcePath) = 0; + virtual IStringTable &GetStringTable() = 0; + + static IBufferManager &Create(NVRenderContext &inRenderContext, IStringTable &inStrTable, + IInputStreamFactory &inInputStreamFactory, + IPerfTimer &inTimer); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp new file mode 100644 index 0000000..72495cd --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderImageBatchLoader.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/StringTable.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderThreadPool.h" +#include "Qt3DSRenderImageScaler.h" +#include "Qt3DSRenderLoadedTexture.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" +#include "foundation/Qt3DSPool.h" +#include "foundation/Qt3DSPerfTimer.h" + +using namespace qt3ds::render; + +namespace { + +struct SImageLoaderBatch; +typedef Mutex::ScopedLock TScopedLock; + +struct SLoadingImage +{ + SImageLoaderBatch *m_Batch; + CRegisteredString m_SourcePath; + QT3DSU64 m_TaskId; + SLoadingImage *m_Tail; + + // Called from main thread + SLoadingImage(CRegisteredString inSourcePath) + : m_Batch(NULL) + , m_SourcePath(inSourcePath) + , m_TaskId(0) + , m_Tail(NULL) + { + } + SLoadingImage() + : m_Batch(NULL) + , m_TaskId(0) + , m_Tail(NULL) + { + } + // Called from main thread + void Setup(SImageLoaderBatch &inBatch); + + // Called from loader thread + static void LoadImage(void *inImg); + + // Potentially called from loader thread + static void TaskCancelled(void *inImg); +}; + +struct SLoadingImageTailOp +{ + SLoadingImage *get(SLoadingImage &inImg) { return inImg.m_Tail; } + void set(SLoadingImage &inImg, SLoadingImage *inItem) { inImg.m_Tail = inItem; } +}; + +typedef InvasiveSingleLinkedList<SLoadingImage, SLoadingImageTailOp> TLoadingImageList; + +struct SBatchLoader; + +struct SImageLoaderBatch +{ + // All variables setup in main thread and constant from then on except + // loaded image count. + SBatchLoader &m_Loader; + NVScopedRefCounted<IImageLoadListener> m_LoadListener; + Sync m_LoadEvent; + Mutex m_LoadMutex; + TLoadingImageList m_Images; + + TImageBatchId m_BatchId; + // Incremented in main thread + QT3DSU32 m_LoadedOrCanceledImageCount; + QT3DSU32 m_FinalizedImageCount; + QT3DSU32 m_NumImages; + NVRenderContextType m_contextType; + bool m_preferKTX; + bool m_ibl; + + // Called from main thread + static SImageLoaderBatch *CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId, + NVConstDataRef<CRegisteredString> inSourcePaths, + CRegisteredString inImageTillLoaded, + IImageLoadListener *inListener, + NVRenderContextType contextType, + bool preferKTX, bool ibl); + + // Called from main thread + SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener, + const TLoadingImageList &inImageList, TImageBatchId inBatchId, + QT3DSU32 inImageCount, NVRenderContextType contextType, + bool preferKTX, bool ibl); + + // Called from main thread + ~SImageLoaderBatch(); + + // Called from main thread + bool IsLoadingFinished() + { + Mutex::ScopedLock __locker(m_LoadMutex); + return m_LoadedOrCanceledImageCount >= m_NumImages; + } + + bool IsFinalizedFinished() + { + Mutex::ScopedLock __locker(m_LoadMutex); + return m_FinalizedImageCount >= m_NumImages; + } + + void IncrementLoadedImageCount() + { + Mutex::ScopedLock __locker(m_LoadMutex); + ++m_LoadedOrCanceledImageCount; + } + void IncrementFinalizedImageCount() + { + Mutex::ScopedLock __locker(m_LoadMutex); + ++m_FinalizedImageCount; + } + // Called from main thread + void Cancel(); + void Cancel(CRegisteredString inSourcePath); +}; + +struct SBatchLoadedImage +{ + CRegisteredString m_SourcePath; + SLoadedTexture *m_Texture; + SImageLoaderBatch *m_Batch; + SBatchLoadedImage() + : m_Texture(NULL) + , m_Batch(NULL) + { + } + + // Called from loading thread + SBatchLoadedImage(CRegisteredString inSourcePath, SLoadedTexture *inTexture, + SImageLoaderBatch &inBatch) + : m_SourcePath(inSourcePath) + , m_Texture(inTexture) + , m_Batch(&inBatch) + { + } + + // Called from main thread + bool Finalize(IBufferManager &inMgr); +}; + +struct SBatchLoader : public IImageBatchLoader +{ + typedef nvhash_map<TImageBatchId, SImageLoaderBatch *> TImageLoaderBatchMap; + typedef nvhash_map<CRegisteredString, TImageBatchId> TSourcePathToBatchMap; + typedef Pool<SLoadingImage, ForwardingAllocator> TLoadingImagePool; + typedef Pool<SImageLoaderBatch, ForwardingAllocator> TBatchPool; + + // Accessed from loader thread + NVFoundationBase &m_Foundation; + volatile QT3DSI32 mRefCount; + // Accessed from loader thread + IInputStreamFactory &m_InputStreamFactory; + //!!Not threadsafe! accessed only from main thread + IBufferManager &m_BufferManager; + // Accessed from main thread + IThreadPool &m_ThreadPool; + // Accessed from both threads + IPerfTimer &m_PerfTimer; + // main thread + TImageBatchId m_NextBatchId; + // main thread + TImageLoaderBatchMap m_Batches; + // main thread + Mutex m_LoaderMutex; + + // Both loader and main threads + nvvector<SBatchLoadedImage> m_LoadedImages; + // main thread + nvvector<TImageBatchId> m_FinishedBatches; + // main thread + TSourcePathToBatchMap m_SourcePathToBatches; + // main thread + nvvector<SLoadingImage> m_LoaderBuilderWorkspace; + TLoadingImagePool m_LoadingImagePool; + TBatchPool m_BatchPool; + + SBatchLoader(NVFoundationBase &inFoundation, IInputStreamFactory &inFactory, + IBufferManager &inBufferManager, IThreadPool &inThreadPool, IPerfTimer &inTimer) + : m_Foundation(inFoundation) + , mRefCount(0) + , m_InputStreamFactory(inFactory) + , m_BufferManager(inBufferManager) + , m_ThreadPool(inThreadPool) + , m_PerfTimer(inTimer) + , m_NextBatchId(1) + , m_Batches(inFoundation.getAllocator(), "SBatchLoader::m_Batches") + , m_LoaderMutex(inFoundation.getAllocator()) + , m_LoadedImages(inFoundation.getAllocator(), "SBatchLoader::m_LoadedImages") + , m_FinishedBatches(inFoundation.getAllocator(), "SBatchLoader::m_FinishedBatches") + , m_SourcePathToBatches(inFoundation.getAllocator(), "SBatchLoader::m_SourcePathToBatches") + , m_LoaderBuilderWorkspace(inFoundation.getAllocator(), + "SBatchLoader::m_LoaderBuilderWorkspace") + , m_LoadingImagePool( + ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_LoadingImagePool")) + , m_BatchPool(ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_BatchPool")) + { + } + + virtual ~SBatchLoader() + { + nvvector<TImageBatchId> theCancelledBatches(m_Foundation.getAllocator(), "~SBatchLoader"); + for (TImageLoaderBatchMap::iterator theIter = m_Batches.begin(), theEnd = m_Batches.end(); + theIter != theEnd; ++theIter) { + theIter->second->Cancel(); + theCancelledBatches.push_back(theIter->second->m_BatchId); + } + for (QT3DSU32 idx = 0, end = theCancelledBatches.size(); idx < end; ++idx) + BlockUntilLoaded(theCancelledBatches[idx]); + + QT3DS_ASSERT(m_Batches.size() == 0); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + // Returns an ID to the load request. Request a block of images to be loaded. + // Also takes an image that the buffer system will return when requested for the given source + // paths + // until said path is loaded. + // An optional listener can be passed in to get callbacks about the batch. + TImageBatchId LoadImageBatch(NVConstDataRef<CRegisteredString> inSourcePaths, + CRegisteredString inImageTillLoaded, + IImageLoadListener *inListener, + NVRenderContextType contextType, + bool preferKTX, bool iblImages) override + { + if (inSourcePaths.size() == 0) + return 0; + + TScopedLock __loaderLock(m_LoaderMutex); + + TImageBatchId theBatchId = 0; + + // Empty loop intentional to find an unused batch id. + for (theBatchId = m_NextBatchId; m_Batches.find(theBatchId) != m_Batches.end(); + ++m_NextBatchId, theBatchId = m_NextBatchId) { + } + + SImageLoaderBatch *theBatch(SImageLoaderBatch::CreateLoaderBatch( + *this, theBatchId, inSourcePaths, inImageTillLoaded, inListener, contextType, + preferKTX, iblImages)); + if (theBatch) { + m_Batches.insert(eastl::make_pair(theBatchId, theBatch)); + return theBatchId; + } + return 0; + } + + void CancelImageBatchLoading(TImageBatchId inBatchId) override + { + SImageLoaderBatch *theBatch(GetBatch(inBatchId)); + if (theBatch) + theBatch->Cancel(); + } + + // Blocks if the image is currently in-flight + void CancelImageLoading(CRegisteredString inSourcePath) override + { + TScopedLock __loaderLock(m_LoaderMutex); + TSourcePathToBatchMap::iterator theIter = m_SourcePathToBatches.find(inSourcePath); + if (theIter != m_SourcePathToBatches.end()) { + TImageBatchId theBatchId = theIter->second; + TImageLoaderBatchMap::iterator theBatchIter = m_Batches.find(theBatchId); + if (theBatchIter != m_Batches.end()) + theBatchIter->second->Cancel(inSourcePath); + } + } + + SImageLoaderBatch *GetBatch(TImageBatchId inId) + { + TScopedLock __loaderLock(m_LoaderMutex); + TImageLoaderBatchMap::iterator theIter = m_Batches.find(inId); + if (theIter != m_Batches.end()) + return theIter->second; + return NULL; + } + + void BlockUntilLoaded(TImageBatchId inId) override + { + for (SImageLoaderBatch *theBatch = GetBatch(inId); theBatch; theBatch = GetBatch(inId)) { + // Only need to block if images aren't loaded. Don't need to block if they aren't + // finalized. + if (!theBatch->IsLoadingFinished()) { + theBatch->m_LoadEvent.wait(200); + theBatch->m_LoadEvent.reset(); + } + BeginFrame(true); + } + } + void ImageLoaded(SLoadingImage &inImage, SLoadedTexture *inTexture) + { + TScopedLock __loaderLock(m_LoaderMutex); + m_LoadedImages.push_back( + SBatchLoadedImage(inImage.m_SourcePath, inTexture, *inImage.m_Batch)); + inImage.m_Batch->IncrementLoadedImageCount(); + inImage.m_Batch->m_LoadEvent.set(); + } + // These are called by the render context, users don't need to call this. + void BeginFrame(bool firstFrame) override + { + TScopedLock __loaderLock(m_LoaderMutex); + // Pass 1 - send out all image loaded signals + for (QT3DSU32 idx = 0, end = m_LoadedImages.size(); idx < end; ++idx) { + + m_SourcePathToBatches.erase(m_LoadedImages[idx].m_SourcePath); + m_LoadedImages[idx].Finalize(m_BufferManager); + m_LoadedImages[idx].m_Batch->IncrementFinalizedImageCount(); + if (m_LoadedImages[idx].m_Batch->IsFinalizedFinished()) + m_FinishedBatches.push_back(m_LoadedImages[idx].m_Batch->m_BatchId); + if (!firstFrame) + break; + } + if (firstFrame) + m_LoadedImages.clear(); + else if (m_LoadedImages.size()) + m_LoadedImages.erase(m_LoadedImages.begin()); + // pass 2 - clean up any existing batches. + for (QT3DSU32 idx = 0, end = m_FinishedBatches.size(); idx < end; ++idx) { + TImageLoaderBatchMap::iterator theIter = m_Batches.find(m_FinishedBatches[idx]); + if (theIter != m_Batches.end()) { + SImageLoaderBatch *theBatch = theIter->second; + if (theBatch->m_LoadListener) + theBatch->m_LoadListener->OnImageBatchComplete(theBatch->m_BatchId); + m_Batches.erase(m_FinishedBatches[idx]); + theBatch->~SImageLoaderBatch(); + m_BatchPool.deallocate(theBatch); + } + } + m_FinishedBatches.clear(); + } + + void EndFrame() override {} +}; + +void SLoadingImage::Setup(SImageLoaderBatch &inBatch) +{ + m_Batch = &inBatch; + m_TaskId = inBatch.m_Loader.m_ThreadPool.AddTask(this, LoadImage, TaskCancelled); +} + +void SLoadingImage::LoadImage(void *inImg) +{ + SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg); + SStackPerfTimer theTimer(theThis->m_Batch->m_Loader.m_PerfTimer, "Image Decompression"); + if (theThis->m_Batch->m_Loader.m_BufferManager.IsImageLoaded(theThis->m_SourcePath) == false) { + SLoadedTexture *theTexture = SLoadedTexture::Load( + theThis->m_SourcePath.c_str(), theThis->m_Batch->m_Loader.m_Foundation, + theThis->m_Batch->m_Loader.m_InputStreamFactory, true, + theThis->m_Batch->m_contextType, + theThis->m_Batch->m_preferKTX); + // if ( theTexture ) + // theTexture->EnsureMultiplerOfFour( theThis->m_Batch->m_Loader.m_Foundation, + //theThis->m_SourcePath.c_str() ); + + theThis->m_Batch->m_Loader.ImageLoaded(*theThis, theTexture); + } else { + theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL); + } +} + +void SLoadingImage::TaskCancelled(void *inImg) +{ + SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg); + theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL); +} + +bool SBatchLoadedImage::Finalize(IBufferManager &inMgr) +{ + if (m_Texture) { + eastl::string thepath(m_SourcePath); + bool isIBL = this->m_Batch->m_ibl; + inMgr.LoadRenderImage(m_SourcePath, *m_Texture, false, isIBL); + inMgr.UnaliasImagePath(m_SourcePath); + } + if (m_Batch->m_LoadListener) + m_Batch->m_LoadListener->OnImageLoadComplete( + m_SourcePath, m_Texture ? ImageLoadResult::Succeeded : ImageLoadResult::Failed); + + if (m_Texture) { + m_Texture->release(); + return true; + } + + return false; +} + +SImageLoaderBatch * +SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId, + NVConstDataRef<CRegisteredString> inSourcePaths, + CRegisteredString inImageTillLoaded, + IImageLoadListener *inListener, + NVRenderContextType contextType, + bool preferKTX, bool iblImages) +{ + TLoadingImageList theImages; + QT3DSU32 theLoadingImageCount = 0; + for (QT3DSU32 idx = 0, end = inSourcePaths.size(); idx < end; ++idx) { + CRegisteredString theSourcePath(inSourcePaths[idx]); + + if (theSourcePath.IsValid() == false) + continue; + + if (inLoader.m_BufferManager.IsImageLoaded(theSourcePath)) + continue; + + eastl::pair<SBatchLoader::TSourcePathToBatchMap::iterator, bool> theInserter = + inLoader.m_SourcePathToBatches.insert(eastl::make_pair(inSourcePaths[idx], inBatchId)); + + // If the loader has already seen this image. + if (theInserter.second == false) + continue; + + if (inImageTillLoaded.IsValid()) { + // Alias the image so any further requests for this source path will result in + // the default images (image till loaded). + bool aliasSuccess = + inLoader.m_BufferManager.AliasImagePath(theSourcePath, inImageTillLoaded, true); + (void)aliasSuccess; + QT3DS_ASSERT(aliasSuccess); + } + + theImages.push_front( + *inLoader.m_LoadingImagePool.construct(theSourcePath, __FILE__, __LINE__)); + ++theLoadingImageCount; + } + if (theImages.empty() == false) { + SImageLoaderBatch *theBatch = + (SImageLoaderBatch *)inLoader.m_BatchPool.allocate(__FILE__, __LINE__); + new (theBatch) + SImageLoaderBatch(inLoader, inListener, theImages, inBatchId, theLoadingImageCount, + contextType, preferKTX, iblImages); + return theBatch; + } + return NULL; +} + +SImageLoaderBatch::SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener, + const TLoadingImageList &inImageList, TImageBatchId inBatchId, + QT3DSU32 inImageCount, NVRenderContextType contextType, + bool preferKTX, bool ibl) + : m_Loader(inLoader) + , m_LoadListener(inLoadListener) + , m_LoadEvent(inLoader.m_Foundation.getAllocator()) + , m_LoadMutex(inLoader.m_Foundation.getAllocator()) + , m_Images(inImageList) + , m_BatchId(inBatchId) + , m_LoadedOrCanceledImageCount(0) + , m_FinalizedImageCount(0) + , m_NumImages(inImageCount) + , m_contextType(contextType) + , m_preferKTX(preferKTX) + , m_ibl(ibl) +{ + for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end; + ++iter) { + iter->Setup(*this); + } +} + +SImageLoaderBatch::~SImageLoaderBatch() +{ + for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end; + ++iter) { + TLoadingImageList::iterator temp(iter); + ++iter; + m_Loader.m_LoadingImagePool.deallocate(temp.m_Obj); + } +} + +void SImageLoaderBatch::Cancel() +{ + for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end; + ++iter) + m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId); +} + +void SImageLoaderBatch::Cancel(CRegisteredString inSourcePath) +{ + for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end; + ++iter) { + if (iter->m_SourcePath == inSourcePath) { + m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId); + break; + } + } +} +} + +IImageBatchLoader &IImageBatchLoader::CreateBatchLoader(NVFoundationBase &inFoundation, + IInputStreamFactory &inFactory, + IBufferManager &inBufferManager, + IThreadPool &inThreadPool, + IPerfTimer &inTimer) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), + SBatchLoader)(inFoundation, inFactory, inBufferManager, inThreadPool, inTimer); +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h new file mode 100644 index 0000000..2805940 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_THREADED_IMAGE_LOADER_H +#define QT3DS_RENDER_THREADED_IMAGE_LOADER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSDataRef.h" +#include "render/Qt3DSRenderBaseTypes.h" + +namespace qt3ds { +namespace render { + struct ImageLoadResult + { + enum Enum { + Succeeded, + Failed, + }; + }; + + class IImageLoadListener : public NVRefCounted + { + protected: + virtual ~IImageLoadListener() {} + + public: + virtual void OnImageLoadComplete(CRegisteredString inPath, + ImageLoadResult::Enum inResult) = 0; + virtual void OnImageBatchComplete(QT3DSU64 inBatch) = 0; + }; + + typedef QT3DSU32 TImageBatchId; + + class IImageBatchLoader : public NVRefCounted + { + protected: + virtual ~IImageBatchLoader() {} + + public: + // Returns an ID to the load request. Request a block of images to be loaded. + // Also takes an image that the buffer system will return when requested for the given + // source paths + // until said path is loaded. + // An optional listener can be passed in to get callbacks about the batch. + virtual TImageBatchId LoadImageBatch(NVConstDataRef<CRegisteredString> inSourcePaths, + CRegisteredString inImageTillLoaded, + IImageLoadListener *inListener, + NVRenderContextType type, + bool preferKTX, bool iblImages) = 0; + // Blocks if any of the images in the batch are in flight + virtual void CancelImageBatchLoading(TImageBatchId inBatchId) = 0; + // Blocks if the image is currently in-flight + virtual void CancelImageLoading(CRegisteredString inSourcePath) = 0; + // Block until every image in the batch is loaded. + virtual void BlockUntilLoaded(TImageBatchId inId) = 0; + + // These are called by the render context, users don't need to call this. + virtual void BeginFrame(bool firstFrame) = 0; + virtual void EndFrame() = 0; + + static IImageBatchLoader &CreateBatchLoader(NVFoundationBase &inFoundation, + IInputStreamFactory &inFactory, + IBufferManager &inBufferManager, + IThreadPool &inThreadPool, IPerfTimer &inTimer); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp new file mode 100644 index 0000000..1176273 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp @@ -0,0 +1,715 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLoadedTexture.h" +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSDMWindowsCompatibility.h" +#include "Qt3DSRenderInputStreamFactory.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSRenderImageScaler.h" +#include "Qt3DSTextRenderer.h" +#include <QImage> + +using namespace qt3ds::render; + +SLoadedTexture *SLoadedTexture::LoadQImage(const QString &inPath, QT3DSI32 flipVertical, + NVFoundationBase &fnd, + NVRenderContextType renderContextType) +{ + Q_UNUSED(flipVertical) + Q_UNUSED(renderContextType) + SLoadedTexture *retval(NULL); + NVAllocatorCallback &alloc(fnd.getAllocator()); + QImage image(inPath); + const QImage::Format format = image.format(); + switch (format) { + case QImage::Format_RGBA64: + image = image.convertToFormat(QImage::Format_RGBA8888); + break; + case QImage::Format_RGBX64: + image = image.convertToFormat(QImage::Format_RGBX8888); + break; + default: + break; + } + image = image.mirrored(); + image = image.rgbSwapped(); + retval = QT3DS_NEW(alloc, SLoadedTexture)(alloc); + retval->width = image.width(); + retval->height = image.height(); + retval->components = image.pixelFormat().channelCount(); + retval->image = image; + retval->data = (void*)retval->image.bits(); + retval->dataSizeInBytes = image.byteCount(); + retval->setFormatFromComponents(); + return retval; +} + + +namespace { + +/** + !!Large section of code ripped from FreeImage!! + +*/ +// ---------------------------------------------------------- +// Structures used by DXT textures +// ---------------------------------------------------------- +typedef QT3DSU8 BYTE; +typedef QT3DSU16 WORD; + +typedef struct tagColor8888 +{ + BYTE b; + BYTE g; + BYTE r; + BYTE a; +} Color8888; + +typedef struct tagColor565 +{ + WORD b : 5; + WORD g : 6; + WORD r : 5; +} Color565; + +typedef struct tagDXTColBlock +{ + Color565 colors[2]; + BYTE row[4]; +} DXTColBlock; + +typedef struct tagDXTAlphaBlockExplicit +{ + WORD row[4]; +} DXTAlphaBlockExplicit; + +typedef struct tagDXTAlphaBlock3BitLinear +{ + BYTE alpha[2]; + BYTE data[6]; +} DXTAlphaBlock3BitLinear; + +typedef struct tagDXT1Block +{ + DXTColBlock color; +} DXT1Block; + +typedef struct tagDXT3Block +{ // also used by dxt2 + DXTAlphaBlockExplicit alpha; + DXTColBlock color; +} DXT3Block; + +typedef struct tagDXT5Block +{ // also used by dxt4 + DXTAlphaBlock3BitLinear alpha; + DXTColBlock color; +} DXT5Block; + +static void GetBlockColors(const DXTColBlock &block, Color8888 colors[4], bool isDXT1) +{ + int i; + for (i = 0; i < 2; i++) { + colors[i].a = 0xff; + colors[i].r = (BYTE)(block.colors[i].r * 0xff / 0x1f); + colors[i].g = (BYTE)(block.colors[i].g * 0xff / 0x3f); + colors[i].b = (BYTE)(block.colors[i].b * 0xff / 0x1f); + } + + WORD *wCol = (WORD *)block.colors; + if (wCol[0] > wCol[1] || !isDXT1) { + // 4 color block + for (i = 0; i < 2; i++) { + colors[i + 2].a = 0xff; + colors[i + 2].r = + (BYTE)((WORD(colors[0].r) * (2 - i) + WORD(colors[1].r) * (1 + i)) / 3); + colors[i + 2].g = + (BYTE)((WORD(colors[0].g) * (2 - i) + WORD(colors[1].g) * (1 + i)) / 3); + colors[i + 2].b = + (BYTE)((WORD(colors[0].b) * (2 - i) + WORD(colors[1].b) * (1 + i)) / 3); + } + } else { + // 3 color block, number 4 is transparent + colors[2].a = 0xff; + colors[2].r = (BYTE)((WORD(colors[0].r) + WORD(colors[1].r)) / 2); + colors[2].g = (BYTE)((WORD(colors[0].g) + WORD(colors[1].g)) / 2); + colors[2].b = (BYTE)((WORD(colors[0].b) + WORD(colors[1].b)) / 2); + + colors[3].a = 0x00; + colors[3].g = 0x00; + colors[3].b = 0x00; + colors[3].r = 0x00; + } +} + +struct DXT_INFO_1 +{ + typedef DXT1Block Block; + enum { isDXT1 = 1, bytesPerBlock = 8 }; +}; + +struct DXT_INFO_3 +{ + typedef DXT3Block Block; + enum { isDXT1 = 1, bytesPerBlock = 16 }; +}; + +struct DXT_INFO_5 +{ + typedef DXT5Block Block; + enum { isDXT1 = 1, bytesPerBlock = 16 }; +}; + +template <class INFO> +class DXT_BLOCKDECODER_BASE +{ +protected: + Color8888 m_colors[4]; + const typename INFO::Block *m_pBlock; + unsigned m_colorRow; + +public: + void Setup(const BYTE *pBlock) + { + m_pBlock = (const typename INFO::Block *)pBlock; + GetBlockColors(m_pBlock->color, m_colors, INFO::isDXT1); + } + + void SetY(int y) { m_colorRow = m_pBlock->color.row[y]; } + + void GetColor(int x, int y, Color8888 &color) + { + Q_UNUSED(y) + unsigned bits = (m_colorRow >> (x * 2)) & 3; + color = m_colors[bits]; + std::swap(color.r, color.b); + } +}; + +class DXT_BLOCKDECODER_1 : public DXT_BLOCKDECODER_BASE<DXT_INFO_1> +{ +public: + typedef DXT_INFO_1 INFO; +}; + +class DXT_BLOCKDECODER_3 : public DXT_BLOCKDECODER_BASE<DXT_INFO_3> +{ +public: + typedef DXT_BLOCKDECODER_BASE<DXT_INFO_3> base; + typedef DXT_INFO_3 INFO; + +protected: + unsigned m_alphaRow; + +public: + void SetY(int y) + { + base::SetY(y); + m_alphaRow = m_pBlock->alpha.row[y]; + } + + void GetColor(int x, int y, Color8888 &color) + { + base::GetColor(x, y, color); + const unsigned bits = (m_alphaRow >> (x * 4)) & 0xF; + color.a = (BYTE)((bits * 0xFF) / 0xF); + } +}; + +class DXT_BLOCKDECODER_5 : public DXT_BLOCKDECODER_BASE<DXT_INFO_5> +{ +public: + typedef DXT_BLOCKDECODER_BASE<DXT_INFO_5> base; + typedef DXT_INFO_5 INFO; + +protected: + unsigned m_alphas[8]; + unsigned m_alphaBits; + int m_offset; + +public: + void Setup(const BYTE *pBlock) + { + base::Setup(pBlock); + + const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha; + m_alphas[0] = block.alpha[0]; + m_alphas[1] = block.alpha[1]; + if (m_alphas[0] > m_alphas[1]) { + // 8 alpha block + for (int i = 0; i < 6; i++) { + m_alphas[i + 2] = ((6 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 3) / 7; + } + } else { + // 6 alpha block + for (int i = 0; i < 4; i++) { + m_alphas[i + 2] = ((4 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 2) / 5; + } + m_alphas[6] = 0; + m_alphas[7] = 0xFF; + } + } + + void SetY(int y) + { + base::SetY(y); + int i = y / 2; + const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha; + m_alphaBits = unsigned(block.data[0 + i * 3]) | (unsigned(block.data[1 + i * 3]) << 8) + | (unsigned(block.data[2 + i * 3]) << 16); + m_offset = (y & 1) * 12; + } + + void GetColor(int x, int y, Color8888 &color) + { + base::GetColor(x, y, color); + unsigned bits = (m_alphaBits >> (x * 3 + m_offset)) & 7; + color.a = (BYTE)m_alphas[bits]; + std::swap(color.r, color.b); + } +}; + +template <class DECODER> +void DecodeDXTBlock(BYTE *dstData, const BYTE *srcBlock, long dstPitch, int bw, int bh) +{ + DECODER decoder; + decoder.Setup(srcBlock); + for (int y = 0; y < bh; y++) { + // Note that this assumes the pointer is pointing to the *last* valid start + // row. + BYTE *dst = dstData - y * dstPitch; + decoder.SetY(y); + for (int x = 0; x < bw; x++) { + decoder.GetColor(x, y, (Color8888 &)*dst); + dst += 4; + } + } +} + +struct STextureDataWriter +{ + QT3DSU32 m_Width; + QT3DSU32 m_Height; + QT3DSU32 m_Stride; + QT3DSU32 m_NumComponents; + STextureData &m_TextureData; + STextureDataWriter(QT3DSU32 w, QT3DSU32 h, bool hasA, STextureData &inTd, NVAllocatorCallback &alloc) + : m_Width(w) + , m_Height(h) + , m_Stride(hasA ? m_Width * 4 : m_Width * 3) + , m_NumComponents(hasA ? 4 : 3) + , m_TextureData(inTd) + { + QT3DSU32 dataSize = m_Stride * m_Height; + if (dataSize > m_TextureData.dataSizeInBytes) { + alloc.deallocate(m_TextureData.data); + m_TextureData.data = + alloc.allocate(dataSize, "SLoadedTexture::DecompressDXTImage", __FILE__, __LINE__); + m_TextureData.dataSizeInBytes = dataSize; + } + memZero(m_TextureData.data, m_TextureData.dataSizeInBytes); + m_TextureData.format = hasA ? NVRenderTextureFormats::RGBA8 : NVRenderTextureFormats::RGB8; + } + + void WritePixel(QT3DSU32 X, QT3DSU32 Y, QT3DSU8 *pixelData) + { + if (X < m_Width && Y < m_Height) { + char *textureData = reinterpret_cast<char *>(m_TextureData.data); + QT3DSU32 offset = Y * m_Stride + X * m_NumComponents; + + for (QT3DSU32 idx = 0; idx < m_NumComponents; ++idx) + QT3DS_ASSERT(textureData[offset + idx] == 0); + + memCopy(textureData + offset, pixelData, m_NumComponents); + } + } + + // Incoming pixels are assumed to be RGBA or RGBX, 32 bit in any case + void WriteBlock(QT3DSU32 X, QT3DSU32 Y, QT3DSU32 width, QT3DSU32 height, QT3DSU8 *pixelData) + { + QT3DSU32 offset = 0; + for (QT3DSU32 yidx = 0; yidx < height; ++yidx) { + for (QT3DSU32 xidx = 0; xidx < width; ++xidx, offset += 4) { + WritePixel(X + xidx, Y + (height - yidx - 1), pixelData + offset); + } + } + } + bool Finished() { return false; } +}; + +struct STextureAlphaScanner +{ + bool &m_Alpha; + + STextureAlphaScanner(bool &inAlpha) + : m_Alpha(inAlpha) + { + } + + void WriteBlock(QT3DSU32 X, QT3DSU32 Y, QT3DSU32 width, QT3DSU32 height, QT3DSU8 *pixelData) + { + Q_UNUSED(X) + Q_UNUSED(Y) + QT3DSU32 offset = 0; + for (QT3DSU32 yidx = 0; yidx < height; ++yidx) { + for (QT3DSU32 xidx = 0; xidx < width; ++xidx, offset += 4) { + if (pixelData[offset + 3] < 255) + m_Alpha = true; + } + } + } + + // If we detect alpha we can stop right there. + bool Finished() { return m_Alpha; } +}; +// Scan the dds image's mipmap 0 level for alpha. +template <class DECODER, class TWriterType> +static void DecompressDDS(void *inSrc, QT3DSU32 inDataSize, QT3DSU32 inWidth, QT3DSU32 inHeight, + TWriterType ioWriter) +{ + typedef typename DECODER::INFO INFO; + typedef typename INFO::Block Block; + (void)inDataSize; + + const QT3DSU8 *pbSrc = (const QT3DSU8 *)inSrc; + // Each DX block is composed of 16 pixels. Free image decodes those + // pixels into a 4x4 block of data. + QT3DSU8 pbDstData[4 * 4 * 4]; + // The decoder decodes backwards + // So we need to point to the last line. + QT3DSU8 *pbDst = pbDstData + 48; + + int width = (int)inWidth; + int height = (int)inHeight; + int lineStride = 16; + for (int y = 0; y < height && ioWriter.Finished() == false; y += 4) { + int yPixels = NVMin(height - y, 4); + for (int x = 0; x < width && ioWriter.Finished() == false; x += 4) { + int xPixels = NVMin(width - x, 4); + DecodeDXTBlock<DECODER>(pbDst, pbSrc, lineStride, xPixels, yPixels); + pbSrc += INFO::bytesPerBlock; + ioWriter.WriteBlock(x, y, xPixels, yPixels, pbDstData); + } + } +} + +bool ScanDDSForAlpha(Qt3DSDDSImage *dds) +{ + bool hasAlpha = false; + switch (dds->format) { + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT1: + DecompressDDS<DXT_BLOCKDECODER_1>(dds->data[0], dds->size[0], dds->mipwidth[0], + dds->mipheight[0], STextureAlphaScanner(hasAlpha)); + break; + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT3: + DecompressDDS<DXT_BLOCKDECODER_3>(dds->data[0], dds->size[0], dds->mipwidth[0], + dds->mipheight[0], STextureAlphaScanner(hasAlpha)); + break; + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT5: + DecompressDDS<DXT_BLOCKDECODER_5>(dds->data[0], dds->size[0], dds->mipwidth[0], + dds->mipheight[0], STextureAlphaScanner(hasAlpha)); + break; + default: + QT3DS_ASSERT(false); + break; + } + return hasAlpha; +} + +bool ScanImageForAlpha(const void *inData, QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inPixelSizeInBytes, + QT3DSU8 inAlphaSizeInBits) +{ + const QT3DSU8 *rowPtr = reinterpret_cast<const QT3DSU8 *>(inData); + bool hasAlpha = false; + if (inAlphaSizeInBits == 0) + return hasAlpha; + if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) { + QT3DS_ASSERT(false); + return false; + } + if (inAlphaSizeInBits > 8) { + QT3DS_ASSERT(false); + return false; + } + + QT3DSU32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits; + QT3DSU32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1; + + for (QT3DSU32 rowIdx = 0; rowIdx < inHeight && hasAlpha == false; ++rowIdx) { + for (QT3DSU32 idx = 0; idx < inWidth && hasAlpha == false; + ++idx, rowPtr += inPixelSizeInBytes) { + QT3DSU32 pixelValue = 0; + if (inPixelSizeInBytes == 2) + pixelValue = *(reinterpret_cast<const QT3DSU16 *>(rowPtr)); + else + pixelValue = *(reinterpret_cast<const QT3DSU32 *>(rowPtr)); + pixelValue = pixelValue >> alphaRightShift; + if (pixelValue < maxAlphaValue) + hasAlpha = true; + } + } + return hasAlpha; +} +} + +SLoadedTexture::~SLoadedTexture() +{ + if (dds) { + if (dds->dataBlock) + QT3DS_FREE(m_Allocator, dds->dataBlock); + + QT3DS_FREE(m_Allocator, dds); + } else if (data && image.byteCount() <= 0) { + m_Allocator.deallocate(data); + } + if (m_Palette) + m_Allocator.deallocate(m_Palette); + if (m_TransparencyTable) + m_Allocator.deallocate(m_TransparencyTable); +} + +void SLoadedTexture::release() +{ + NVAllocatorCallback *theAllocator(&m_Allocator); + this->~SLoadedTexture(); + theAllocator->deallocate(this); +} + +bool SLoadedTexture::ScanForTransparency() +{ + switch (format) { + case NVRenderTextureFormats::SRGB8A8: + case NVRenderTextureFormats::RGBA8: + if (!data) { // dds + return true; + } else { + return ScanImageForAlpha(data, width, height, 4, 8); + } + break; + // Scan the image. + case NVRenderTextureFormats::SRGB8: + case NVRenderTextureFormats::RGB8: + return false; + break; + case NVRenderTextureFormats::RGB565: + return false; + break; + case NVRenderTextureFormats::RGBA5551: + if (!data) { // dds + return true; + } else { + return ScanImageForAlpha(data, width, height, 2, 1); + } + break; + case NVRenderTextureFormats::Alpha8: + return true; + break; + case NVRenderTextureFormats::Luminance8: + return false; + break; + case NVRenderTextureFormats::LuminanceAlpha8: + if (!data) { // dds + return true; + } else { + return ScanImageForAlpha(data, width, height, 2, 8); + } + break; + case NVRenderTextureFormats::RGB_DXT1: + return false; + break; + case NVRenderTextureFormats::RGBA_DXT3: + case NVRenderTextureFormats::RGBA_DXT1: + case NVRenderTextureFormats::RGBA_DXT5: + if (dds) { + return ScanDDSForAlpha(dds); + } else { + QT3DS_ASSERT(false); + return false; + } + break; + case NVRenderTextureFormats::RGB9E5: + return false; + break; + case NVRenderTextureFormats::RG32F: + case NVRenderTextureFormats::RGB32F: + case NVRenderTextureFormats::RGBA16F: + case NVRenderTextureFormats::RGBA32F: + // PKC TODO : For now, since IBL will be the main consumer, we'll just pretend there's no + // alpha. + // Need to do a proper scan down the line, but doing it for floats is a little different + // from + // integer scans. + return false; + break; + default: + break; + } + QT3DS_ASSERT(false); + return false; +} + +void SLoadedTexture::EnsureMultiplerOfFour(NVFoundationBase &inFoundation, const char *inPath) +{ + if (width % 4 || height % 4) { + qCWarning(PERF_WARNING, + "Image %s has non multiple of four width or height; perf hit for scaling", inPath); + if (data) { + QT3DSU32 newWidth = ITextRenderer::NextMultipleOf4(width); + QT3DSU32 newHeight = ITextRenderer::NextMultipleOf4(height); + QT3DSU32 newDataSize = newWidth * newHeight * components; + NVAllocatorCallback &theAllocator(inFoundation.getAllocator()); + QT3DSU8 *newData = (QT3DSU8 *)(theAllocator.allocate(newDataSize, "Scaled Image Data", + __FILE__, __LINE__)); + CImageScaler theScaler(theAllocator); + if (components == 4) { + theScaler.FastExpandRowsAndColumns((unsigned char *)data, width, height, newData, + newWidth, newHeight); + } else + theScaler.ExpandRowsAndColumns((unsigned char *)data, width, height, newData, + newWidth, newHeight, components); + + theAllocator.deallocate(data); + data = newData; + width = newWidth; + height = newHeight; + dataSizeInBytes = newDataSize; + } + } +} + +STextureData SLoadedTexture::DecompressDXTImage(int inMipMapIdx, STextureData *inOptLastImage) +{ + STextureData retval; + if (inOptLastImage) + retval = *inOptLastImage; + + if (dds == NULL || inMipMapIdx >= dds->numMipmaps) { + QT3DS_ASSERT(false); + ReleaseDecompressedTexture(retval); + return STextureData(); + } + char *srcData = (char *)dds->data[inMipMapIdx]; + int srcDataSize = dds->size[inMipMapIdx]; + QT3DSU32 imgWidth = (QT3DSU32)dds->mipwidth[inMipMapIdx]; + QT3DSU32 imgHeight = (QT3DSU32)dds->mipheight[inMipMapIdx]; + + switch (format) { + case NVRenderTextureFormats::RGB_DXT1: + DecompressDDS<DXT_BLOCKDECODER_1>( + srcData, srcDataSize, imgWidth, imgHeight, + STextureDataWriter(imgWidth, imgHeight, false, retval, m_Allocator)); + break; + case NVRenderTextureFormats::RGBA_DXT1: + DecompressDDS<DXT_BLOCKDECODER_1>( + srcData, srcDataSize, imgWidth, imgHeight, + STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator)); + break; + case NVRenderTextureFormats::RGBA_DXT3: + DecompressDDS<DXT_BLOCKDECODER_3>( + srcData, srcDataSize, imgWidth, imgHeight, + STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator)); + break; + case NVRenderTextureFormats::RGBA_DXT5: + DecompressDDS<DXT_BLOCKDECODER_5>( + srcData, srcDataSize, imgWidth, imgHeight, + STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator)); + break; + default: + QT3DS_ASSERT(false); + break; + } + return retval; +} + +void SLoadedTexture::ReleaseDecompressedTexture(STextureData inImage) +{ + if (inImage.data) + m_Allocator.deallocate(inImage.data); +} + +#ifndef EA_PLATFORM_WINDOWS +#define stricmp strcasecmp +#endif + +SLoadedTexture *SLoadedTexture::Load(const QString &inPath, NVFoundationBase &inFoundation, + IInputStreamFactory &inFactory, bool inFlipY, + NVRenderContextType renderContextType, bool preferKTX) +{ + if (inPath.isEmpty()) + return nullptr; + + // Check KTX path first + QString path = inPath; + QString ktxSource = inPath; + if (preferKTX) { + ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.'))); + ktxSource.append(QLatin1String(".ktx")); + } + + SLoadedTexture *theLoadedImage = nullptr; + // We will get invalid error logs of files not found if we don't force quiet mode + // If the file is actually missing, it will be logged later (loaded image is null) + NVScopedRefCounted<IRefCountedInputStream> theStream( + inFactory.GetStreamForFile(preferKTX ? ktxSource : inPath, true)); + if (!theStream.mPtr) { + if (preferKTX) + theStream = inFactory.GetStreamForFile(inPath, true); + else + return nullptr; + } else { + path = ktxSource; + } + QString fileName; + inFactory.GetPathForFile(path, fileName, true); + if (theStream.mPtr && path.size() > 3) { + if (path.endsWith(QLatin1String("png"), Qt::CaseInsensitive) + || path.endsWith(QLatin1String("jpg"), Qt::CaseInsensitive) + || path.endsWith(QLatin1String("peg"), Qt::CaseInsensitive)) { + theLoadedImage = LoadQImage(fileName, inFlipY, inFoundation, renderContextType); + } else if (path.endsWith(QLatin1String("dds"), Qt::CaseInsensitive)) { + theLoadedImage = LoadDDS(*theStream, inFlipY, inFoundation, renderContextType); + } else if (path.endsWith(QLatin1String("gif"), Qt::CaseInsensitive)) { + theLoadedImage = LoadGIF(*theStream, !inFlipY, inFoundation, renderContextType); + } else if (path.endsWith(QLatin1String("bmp"), Qt::CaseInsensitive)) { + theLoadedImage = LoadBMP(*theStream, !inFlipY, inFoundation, renderContextType); + } else if (path.endsWith(QLatin1String("hdr"), Qt::CaseInsensitive)) { + theLoadedImage = LoadHDR(*theStream, inFoundation, renderContextType); + } else if (path.endsWith(QLatin1String("ktx"), Qt::CaseInsensitive)) { + theLoadedImage = LoadKTX(*theStream, inFlipY, inFoundation, renderContextType); + } else { + qCWarning(INTERNAL_ERROR, "Unrecognized image extension: %s", qPrintable(inPath)); + } + } + + return theLoadedImage; +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h new file mode 100644 index 0000000..f019400 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_LOADED_TEXTURE_H +#define QT3DS_RENDER_LOADED_TEXTURE_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "Qt3DSRenderLoadedTextureDDS.h" +#include "foundation/Qt3DSRefCounted.h" +#include <QImage> + +namespace qt3ds { +namespace foundation { + class ISeekableIOStream; + class IInStream; +} +} + +namespace qt3ds { +namespace render { + + class IInputStreamFactory; + + struct STextureData + { + void *data; + QT3DSU32 dataSizeInBytes; + qt3ds::render::NVRenderTextureFormats::Enum format; + STextureData() + : data(NULL) + , dataSizeInBytes(0) + , format(qt3ds::render::NVRenderTextureFormats::Unknown) + { + } + }; + struct ExtendedTextureFormats + { + enum Enum { + NoExtendedFormat = 0, + Palettized, + CustomRGB, + }; + }; + // Utility class used for loading image data from disk. + // Supports jpg, png, and dds. + struct SLoadedTexture : public NVReleasable + { + private: + ~SLoadedTexture(); + + public: + NVAllocatorCallback &m_Allocator; + QT3DSI32 width; + QT3DSI32 height; + QT3DSI32 components; + void *data; + QImage image; + QT3DSU32 dataSizeInBytes; + qt3ds::render::NVRenderTextureFormats::Enum format; + Qt3DSDDSImage *dds; + ExtendedTextureFormats::Enum m_ExtendedFormat; + // Used for palettized images. + void *m_Palette; + QT3DSI32 m_CustomMasks[3]; + int m_BitCount; + char8_t m_BackgroundColor[3]; + uint8_t *m_TransparencyTable; + int32_t m_TransparentPaletteIndex; + + SLoadedTexture(NVAllocatorCallback &inAllocator) + : m_Allocator(inAllocator) + , width(0) + , height(0) + , components(0) + , data(NULL) + , image(0) + , dataSizeInBytes(0) + , format(qt3ds::render::NVRenderTextureFormats::RGBA8) + , dds(NULL) + , m_ExtendedFormat(ExtendedTextureFormats::NoExtendedFormat) + , m_Palette(NULL) + , m_BitCount(0) + , m_TransparencyTable(NULL) + , m_TransparentPaletteIndex(-1) + { + m_CustomMasks[0] = 0; + m_CustomMasks[1] = 0; + m_CustomMasks[2] = 0; + m_BackgroundColor[0] = 0; + m_BackgroundColor[1] = 0; + m_BackgroundColor[2] = 0; + } + void setFormatFromComponents() + { + switch (components) { + case 1: // undefined, but in this context probably luminance + format = qt3ds::render::NVRenderTextureFormats::Luminance8; + break; + case 2: + format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8; + break; + case 3: + format = qt3ds::render::NVRenderTextureFormats::RGB8; + break; + + default: + // fallthrough intentional + case 4: + format = qt3ds::render::NVRenderTextureFormats::RGBA8; + break; + } + } + + void EnsureMultiplerOfFour(NVFoundationBase &inFoundation, const char *inPath); + // Returns true if this image has a pixel less than 255. + bool ScanForTransparency(); + + // Be sure to call this or risk leaking an enormous amount of memory + void release() override; + + // Not all video cards support dxt compression. Giving the last image allows + // this object to potentially reuse the memory + STextureData DecompressDXTImage(int inMipMapIdx, STextureData *inOptLastImage = NULL); + void ReleaseDecompressedTexture(STextureData inImage); + + static SLoadedTexture *Load(const QString &inPath, NVFoundationBase &inAllocator, + IInputStreamFactory &inFactory, bool inFlipY = true, + NVRenderContextType renderContextType + = NVRenderContextValues::NullContext, bool preferKTX = false); + static SLoadedTexture *LoadDDS(IInStream &inStream, QT3DSI32 flipVertical, + NVFoundationBase &fnd, + NVRenderContextType renderContextType); + static SLoadedTexture *LoadKTX(IInStream &inStream, QT3DSI32 flipVertical, + NVFoundationBase &fnd, + NVRenderContextType renderContextType); + static SLoadedTexture *LoadBMP(ISeekableIOStream &inStream, bool inFlipY, + NVFoundationBase &inFnd, + NVRenderContextType renderContextType); + static SLoadedTexture *LoadGIF(ISeekableIOStream &inStream, bool inFlipY, + NVFoundationBase &inFnd, + NVRenderContextType renderContextType); + static SLoadedTexture *LoadHDR(ISeekableIOStream &inStream, NVFoundationBase &inFnd, + NVRenderContextType renderContextType); + + static SLoadedTexture *LoadQImage(const QString &inPath, QT3DSI32 flipVertical, + NVFoundationBase &fnd, + NVRenderContextType renderContextType); + + private: + // Implemented in the bmp loader. + void FreeImagePostProcess(bool inFlipY); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp new file mode 100644 index 0000000..29e75a8 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp @@ -0,0 +1,1262 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +// ========================================================== +// BMP Loader and Writer +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Markus Loibl (markus.loibl@epost.de) +// - Martin Weber (martweb@gmx.net) +// - Herve Drolon (drolon@infonie.fr) +// - Michal Novotny (michal@etc.cz) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "Qt3DSRenderLoadedTextureFreeImageCompat.h" + +// ---------------------------------------------------------- +// Constants + headers +// ---------------------------------------------------------- + +static const BYTE RLE_COMMAND = 0; +static const BYTE RLE_ENDOFLINE = 0; +static const BYTE RLE_ENDOFBITMAP = 1; +static const BYTE RLE_DELTA = 2; + +static const BYTE BI_RGB = 0; +static const BYTE BI_RLE8 = 1; +static const BYTE BI_RLE4 = 2; +static const BYTE BI_BITFIELDS = 3; + +// ---------------------------------------------------------- + +#ifdef _WIN32 +#pragma pack(push, 1) +#else +#pragma pack(1) +#endif + +typedef struct tagBITMAPCOREHEADER +{ + DWORD bcSize; + WORD bcWidth; + WORD bcHeight; + WORD bcPlanes; + WORD bcBitCnt; +} BITMAPCOREHEADER, *PBITMAPCOREHEADER; + +typedef struct tagBITMAPINFOOS2_1X_HEADER +{ + DWORD biSize; + WORD biWidth; + WORD biHeight; + WORD biPlanes; + WORD biBitCount; +} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER; + +typedef struct tagBITMAPFILEHEADER +{ + WORD bfType; + DWORD bfSize; + WORD bfReserved1; + WORD bfReserved2; + DWORD bfOffBits; +} BITMAPFILEHEADER, *PBITMAPFILEHEADER; + + +#ifdef _WIN32 +#pragma pack(pop) +#else +#pragma pack() +#endif + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Internal functions +// ========================================================== + +#ifdef FREEIMAGE_BIGENDIAN +static void SwapInfoHeader(BITMAPINFOHEADER *header) +{ + SwapLong(&header->biSize); + SwapLong((DWORD *)&header->biWidth); + SwapLong((DWORD *)&header->biHeight); + SwapShort(&header->biPlanes); + SwapShort(&header->biBitCount); + SwapLong(&header->biCompression); + SwapLong(&header->biSizeImage); + SwapLong((DWORD *)&header->biXPelsPerMeter); + SwapLong((DWORD *)&header->biYPelsPerMeter); + SwapLong(&header->biClrUsed); + SwapLong(&header->biClrImportant); +} + +static void SwapCoreHeader(BITMAPCOREHEADER *header) +{ + SwapLong(&header->bcSize); + SwapShort(&header->bcWidth); + SwapShort(&header->bcHeight); + SwapShort(&header->bcPlanes); + SwapShort(&header->bcBitCnt); +} + +static void SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header) +{ + SwapLong(&header->biSize); + SwapShort(&header->biWidth); + SwapShort(&header->biHeight); + SwapShort(&header->biPlanes); + SwapShort(&header->biBitCount); +} + +static void SwapFileHeader(BITMAPFILEHEADER *header) +{ + SwapShort(&header->bfType); + SwapLong(&header->bfSize); + SwapShort(&header->bfReserved1); + SwapShort(&header->bfReserved2); + SwapLong(&header->bfOffBits); +} +#endif + +// -------------------------------------------------------------------------- + +/** +Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib +@param io FreeImage IO +@param handle FreeImage IO handle +@param dib Image to be loaded +@param height Image height +@param pitch Image pitch +@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit) +*/ +static void LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, int pitch, + int bit_count) +{ + (void)bit_count; + // Load pixel data + // NB: height can be < 0 for BMP data + if (height > 0) { + io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); + } else { + int positiveHeight = abs(height); + for (int c = 0; c < positiveHeight; ++c) { + io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, + handle); + } + } + +// swap as needed +#ifdef FREEIMAGE_BIGENDIAN + if (bit_count == 16) { + for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { + WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); + for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { + SwapShort(pixel); + pixel++; + } + } + } +#endif + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + if (bit_count == 24 || bit_count == 32) { + for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { + BYTE *pixel = FreeImage_GetScanLine(dib, y); + for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { + INPLACESWAP(pixel[0], pixel[2]); + pixel += (bit_count >> 3); + } + } + } +#endif +} + +/** +Load image pixels for 4-bit RLE compressed dib +@param io FreeImage IO +@param handle FreeImage IO handle +@param width Image width +@param height Image height +@param dib Image to be loaded +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height, + FIBITMAP *dib) +{ + int status_byte = 0; + BYTE second_byte = 0; + int bits = 0; + + BYTE *pixels = NULL; // temporary 8-bit buffer + + try { + height = abs(height); + + pixels = (BYTE *)malloc(width * height * sizeof(BYTE)); + if (!pixels) + throw(1); + memset(pixels, 0, width * height * sizeof(BYTE)); + + BYTE *q = pixels; + BYTE *end = pixels + height * width; + + for (int scanline = 0; scanline < height;) { + if (q < pixels || q >= end) { + break; + } + if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + if (status_byte != 0) { + status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q)); + // Encoded mode + if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + for (int i = 0; i < status_byte; i++) { + *q++ = (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f)); + } + bits += status_byte; + } else { + // Escape mode + if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + switch (status_byte) { + case RLE_ENDOFLINE: { + // End of line + bits = 0; + scanline++; + q = pixels + scanline * width; + } break; + + case RLE_ENDOFBITMAP: + // End of bitmap + q = end; + break; + + case RLE_DELTA: { + // read the delta values + + BYTE delta_x = 0; + BYTE delta_y = 0; + + if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + + // apply them + + bits += delta_x; + scanline += delta_y; + q = pixels + scanline * width + bits; + } break; + + default: { + // Absolute mode + status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q)); + for (int i = 0; i < status_byte; i++) { + if ((i & 0x01) == 0) { + if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + } + *q++ = + (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f)); + } + bits += status_byte; + // Read pad byte + if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) { + BYTE padding = 0; + if (io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) { + throw(1); + } + } + } break; + } + } + } + + { + // Convert to 4-bit + for (int y = 0; y < height; y++) { + const BYTE *src = (BYTE *)pixels + y * width; + BYTE *dst = FreeImage_GetScanLine(dib, y); + + BOOL hinibble = TRUE; + + for (int cols = 0; cols < width; cols++) { + if (hinibble) { + dst[cols >> 1] = (src[cols] << 4); + } else { + dst[cols >> 1] |= src[cols]; + } + + hinibble = !hinibble; + } + } + } + + free(pixels); + + return TRUE; + + } catch (int) { + if (pixels) + free(pixels); + return FALSE; + } +} + +/** +Load image pixels for 8-bit RLE compressed dib +@param io FreeImage IO +@param handle FreeImage IO handle +@param width Image width +@param height Image height +@param dib Image to be loaded +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height, + FIBITMAP *dib) +{ + BYTE status_byte = 0; + BYTE second_byte = 0; + int scanline = 0; + int bits = 0; + + for (;;) { + if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + + switch (status_byte) { + case RLE_COMMAND: + if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + + switch (status_byte) { + case RLE_ENDOFLINE: + bits = 0; + scanline++; + break; + + case RLE_ENDOFBITMAP: + return TRUE; + + case RLE_DELTA: { + // read the delta values + + BYTE delta_x = 0; + BYTE delta_y = 0; + + if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + + // apply them + + bits += delta_x; + scanline += delta_y; + + break; + } + + default: { + if (scanline >= abs(height)) { + return TRUE; + } + + int count = MIN((int)status_byte, width - bits); + + BYTE *sline = FreeImage_GetScanLine(dib, scanline); + + if (io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) { + return FALSE; + } + + // align run length to even number of bytes + + if ((status_byte & 1) == 1) { + if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + } + + bits += status_byte; + + break; + } + } + + break; + + default: { + if (scanline >= abs(height)) { + return TRUE; + } + + int count = MIN((int)status_byte, width - bits); + + BYTE *sline = FreeImage_GetScanLine(dib, scanline); + + if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) { + return FALSE; + } + + for (int i = 0; i < count; i++) { + *(sline + bits) = second_byte; + + bits++; + } + + break; + } + } + } +} + +// -------------------------------------------------------------------------- + +static FIBITMAP *LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, + unsigned bitmap_bits_offset) +{ + FIBITMAP *dib = NULL; + (void)flags; + try { + // load the info header + + BITMAPINFOHEADER bih; + + io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapInfoHeader(&bih); +#endif + + // keep some general information about the bitmap + + int used_colors = bih.biClrUsed; + int width = bih.biWidth; + int height = bih.biHeight; // WARNING: height can be < 0 => check each call using 'height' + // as a parameter + int alloc_height = abs(height); + int bit_count = bih.biBitCount; + int compression = bih.biCompression; + int pitch = CalculatePitch(CalculateLine(width, bit_count)); + + switch (bit_count) { + case 1: + case 4: + case 8: { + if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) + used_colors = CalculateUsedPaletteEntries(bit_count); + + // allocate enough memory to hold the bitmap (header, palette, pixels) and read the + // palette + + dib = FreeImage_Allocate(width, alloc_height, bit_count, io); + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + // load the palette + + io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle); +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + RGBQUAD *pal = FreeImage_GetPalette(dib); + for (int i = 0; i < used_colors; i++) { + INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue); + } +#endif + + // seek to the actual pixel data. + // this is needed because sometimes the palette is larger than the entries it contains + // predicts + + if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + + (used_colors * sizeof(RGBQUAD)))) + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + + // read the pixel data + + switch (compression) { + case BI_RGB: + LoadPixelData(io, handle, dib, height, pitch, bit_count); + return dib; + + case BI_RLE4: + if (LoadPixelDataRLE4(io, handle, width, height, dib)) { + return dib; + } else { + throw "Error encountered while decoding RLE4 BMP data"; + } + break; + + case BI_RLE8: + if (LoadPixelDataRLE8(io, handle, width, height, dib)) { + return dib; + } else { + throw "Error encountered while decoding RLE8 BMP data"; + } + break; + + default: + throw "compression type not supported"; + } + } break; // 1-, 4-, 8-bit + + case 16: { + if (bih.biCompression == BI_BITFIELDS) { + DWORD bitfields[3]; + + io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); + + dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1], + bitfields[2], io); + } else { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK, + FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io); + } + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + return dib; + } break; // 16-bit + + case 24: + case 32: { + if (bih.biCompression == BI_BITFIELDS) { + DWORD bitfields[3]; + + io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); + + dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1], + bitfields[2], io); + } else { + if (bit_count == 32) { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } else { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } + } + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + // Skip over the optional palette + // A 24 or 32 bit DIB may contain a palette for faster color reduction + + if (used_colors > 0) { + io->seek_proc(handle, used_colors * sizeof(RGBQUAD), SEEK_CUR); + } else if ((bih.biCompression != BI_BITFIELDS) + && (bitmap_bits_offset + > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) { + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + } + + // read in the bitmap bits + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + // check if the bitmap contains transparency, if so enable it in the header + + return dib; + } break; // 24-, 32-bit + } + } catch (const char *message) { + if (dib) { + FreeImage_Unload(dib); + } + if (message) { + FreeImage_OutputMessageProc(s_format_id, message, io); + } + } + + return NULL; +} + +// -------------------------------------------------------------------------- + +static FIBITMAP *LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, + unsigned bitmap_bits_offset) +{ + FIBITMAP *dib = NULL; + (void)flags; + try { + // load the info header + + BITMAPINFOHEADER bih; + + io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapInfoHeader(&bih); +#endif + + // keep some general information about the bitmap + + int used_colors = bih.biClrUsed; + int width = bih.biWidth; + int height = bih.biHeight; // WARNING: height can be < 0 => check each read_proc using + // 'height' as a parameter + int alloc_height = abs(height); + int bit_count = bih.biBitCount; + int compression = bih.biCompression; + int pitch = CalculatePitch(CalculateLine(width, bit_count)); + + switch (bit_count) { + case 1: + case 4: + case 8: { + if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) + used_colors = CalculateUsedPaletteEntries(bit_count); + + // allocate enough memory to hold the bitmap (header, palette, pixels) and read the + // palette + + dib = FreeImage_Allocate(width, alloc_height, bit_count, io); + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + // load the palette + + io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET); + + RGBQUAD *pal = FreeImage_GetPalette(dib); + + for (int count = 0; count < used_colors; count++) { + FILE_BGR bgr; + + io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); + + pal[count].rgbRed = bgr.r; + pal[count].rgbGreen = bgr.g; + pal[count].rgbBlue = bgr.b; + } + + // seek to the actual pixel data. + // this is needed because sometimes the palette is larger than the entries it contains + // predicts + + if (bitmap_bits_offset + > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + + // read the pixel data + + switch (compression) { + case BI_RGB: + // load pixel data + LoadPixelData(io, handle, dib, height, pitch, bit_count); + return dib; + + case BI_RLE4: + if (LoadPixelDataRLE4(io, handle, width, height, dib)) { + return dib; + } else { + throw "Error encountered while decoding RLE4 BMP data"; + } + break; + + case BI_RLE8: + if (LoadPixelDataRLE8(io, handle, width, height, dib)) { + return dib; + } else { + throw "Error encountered while decoding RLE8 BMP data"; + } + break; + + default: + throw "compression type not supported"; + } + } + + case 16: { + if (bih.biCompression == 3) { + DWORD bitfields[3]; + + io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); + + dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1], + bitfields[2], io); + } else { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK, + FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io); + } + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + if (bitmap_bits_offset + > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) { + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + } + + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + return dib; + } + + case 24: + case 32: { + if (bit_count == 32) { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } else { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information + FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter); + FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter); + + // Skip over the optional palette + // A 24 or 32 bit DIB may contain a palette for faster color reduction + + if (bitmap_bits_offset + > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + + // read in the bitmap bits + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + return dib; + } + } + } catch (const char *message) { + if (dib) + FreeImage_Unload(dib); + + FreeImage_OutputMessageProc(s_format_id, message, io); + } + + return NULL; +} + +// -------------------------------------------------------------------------- + +static FIBITMAP *LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, + unsigned bitmap_bits_offset) +{ + FIBITMAP *dib = NULL; + (void)flags; + try { + BITMAPINFOOS2_1X_HEADER bios2_1x; + + io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapOS21XHeader(&bios2_1x); +#endif + // keep some general information about the bitmap + + int used_colors = 0; + int width = bios2_1x.biWidth; + int height = bios2_1x.biHeight; // WARNING: height can be < 0 => check each read_proc using + // 'height' as a parameter + int alloc_height = abs(height); + int bit_count = bios2_1x.biBitCount; + int pitch = CalculatePitch(CalculateLine(width, bit_count)); + + switch (bit_count) { + case 1: + case 4: + case 8: { + used_colors = CalculateUsedPaletteEntries(bit_count); + + // allocate enough memory to hold the bitmap (header, palette, pixels) and read the + // palette + + dib = FreeImage_Allocate(width, alloc_height, bit_count, io); + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information to default values (72 dpi in english units) + FreeImage_SetDotsPerMeterX(dib, 2835); + FreeImage_SetDotsPerMeterY(dib, 2835); + + // load the palette + + RGBQUAD *pal = FreeImage_GetPalette(dib); + + for (int count = 0; count < used_colors; count++) { + FILE_BGR bgr; + + io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); + + pal[count].rgbRed = bgr.r; + pal[count].rgbGreen = bgr.g; + pal[count].rgbBlue = bgr.b; + } + + // Skip over the optional palette + // A 24 or 32 bit DIB may contain a palette for faster color reduction + + io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + + // read the pixel data + + // load pixel data + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + return dib; + } + + case 16: { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK, + FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io); + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information to default values (72 dpi in english units) + FreeImage_SetDotsPerMeterX(dib, 2835); + FreeImage_SetDotsPerMeterY(dib, 2835); + + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + return dib; + } + + case 24: + case 32: { + if (bit_count == 32) { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } else { + dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io); + } + + if (dib == NULL) + throw "DIB allocation failed"; + + // set resolution information to default values (72 dpi in english units) + FreeImage_SetDotsPerMeterX(dib, 2835); + FreeImage_SetDotsPerMeterY(dib, 2835); + + // Skip over the optional palette + // A 24 or 32 bit DIB may contain a palette for faster color reduction + + // load pixel data and swap as needed if OS is Big Endian + LoadPixelData(io, handle, dib, height, pitch, bit_count); + + // check if the bitmap contains transparency, if so enable it in the header + + return dib; + } + } + } catch (const char *message) { + if (dib) + FreeImage_Unload(dib); + + FreeImage_OutputMessageProc(s_format_id, message, io); + } + + return NULL; +} + +// ========================================================== +// Plugin Implementation +// ========================================================== + +// ---------------------------------------------------------- + +static FIBITMAP *DoLoadBMP(FreeImageIO *io, fi_handle handle, int flags) +{ + if (handle != NULL) { + BITMAPFILEHEADER bitmapfileheader; + DWORD type = 0; + BYTE magic[2]; + + // we use this offset value to make seemingly absolute seeks relative in the file + + long offset_in_file = io->tell_proc(handle); + + // read the magic + + io->read_proc(&magic, sizeof(magic), 1, handle); + + // compare the magic with the number we know + + // somebody put a comment here explaining the purpose of this loop + while (memcmp(&magic, "BA", 2) == 0) { + io->read_proc(&bitmapfileheader.bfSize, sizeof(DWORD), 1, handle); + io->read_proc(&bitmapfileheader.bfReserved1, sizeof(WORD), 1, handle); + io->read_proc(&bitmapfileheader.bfReserved2, sizeof(WORD), 1, handle); + io->read_proc(&bitmapfileheader.bfOffBits, sizeof(DWORD), 1, handle); + io->read_proc(&magic, sizeof(magic), 1, handle); + } + + // read the fileheader + + io->seek_proc(handle, (0 - (int)sizeof(magic)), SEEK_CUR); + io->read_proc(&bitmapfileheader, (int)sizeof(BITMAPFILEHEADER), 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapFileHeader(&bitmapfileheader); +#endif + + // read the first byte of the infoheader + + io->read_proc(&type, sizeof(DWORD), 1, handle); + io->seek_proc(handle, 0 - (int)sizeof(DWORD), SEEK_CUR); +#ifdef FREEIMAGE_BIGENDIAN + SwapLong(&type); +#endif + + // call the appropriate load function for the found bitmap type + + if (type == 40) + return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); + + if (type == 12) + return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); + + if (type <= 64) + return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); + + char buf[256]; + sprintf(buf, "unknown bmp subtype with id %d", type); + FreeImage_OutputMessageProc(s_format_id, buf, io); + } + + return NULL; +} + +template <QT3DSU32 TBitWidth> +struct SPaletteIndexer +{ + static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos) + { + uint32_t divisor = 8 / TBitWidth; + uint32_t byte = inPos / divisor; + uint32_t modulus = inPos % divisor; + uint32_t shift = TBitWidth * modulus; + uint32_t mask = (1 << TBitWidth) - 1; + mask = mask << shift; + uint32_t byteData = inData[byte]; + return (byteData & mask) >> shift; + } +}; + +template <> +struct SPaletteIndexer<1> +{ + static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos) + { + uint32_t byte = (inPos / 8); + uint32_t bit = 1 << (7 - (inPos % 8)); + uint32_t byteData = inData[byte]; + return (byteData & bit) ? 1 : 0; + } +}; + +template <> +struct SPaletteIndexer<8> +{ + static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos) + { + uint32_t byte = inPos; + uint32_t bit = 0xFF; + uint32_t byteData = inData[byte]; + return byteData & bit; + } +}; + +static inline void assignQuad(uint8_t *dest, const RGBQUAD &quad) +{ + dest[0] = quad.rgbRed; + dest[1] = quad.rgbGreen; + dest[2] = quad.rgbBlue; +} + +template <QT3DSU32 bitCount> +inline void LoadPalettized(bool inFlipY, const RGBQUAD *palette, void *data, uint8_t *newData, + int width, int height, int components, int transparentIndex) +{ + const uint8_t *oldData = (const uint8_t *)data; + int pitch = CalculatePitch(CalculateLine(width, bitCount)); + for (uint32_t h = 0; h < (uint32_t)height; ++h) { + uint32_t relHeight = h; + if (inFlipY) + relHeight = ((uint32_t)height) - h - 1; + for (uint32_t w = 0; w < (uint32_t)width; ++w) { + const uint8_t *dataLine = oldData + pitch * h; + uint32_t pos = width * relHeight + w; + uint32_t paletteIndex = SPaletteIndexer<bitCount>::IndexOf(dataLine, w); + const RGBQUAD &theQuad = palette[paletteIndex]; + uint8_t *writePtr = newData + (pos * components); + assignQuad(writePtr, theQuad); + if (paletteIndex == (uint32_t)transparentIndex && components == 4) { + writePtr[3] = 0; + } + } + } +} + +inline int firstHighBit(int data) +{ + if (data == 0) + return 0; + int idx = 0; + while ((data % 2) == 0) { + data = data >> 1; + ++idx; + } + return idx; +} + +struct SMaskData +{ + uint32_t mask; + uint32_t shift; + uint32_t max; + + SMaskData(int inMask) + { + mask = inMask; + shift = firstHighBit(mask); + max = mask >> shift; + } + + inline uint8_t MapColor(uint32_t color) const + { + uint32_t intermediateValue = (color & mask) >> shift; + return (uint8_t)((intermediateValue * 255) / max); + } +}; + +template <QT3DSU32> +struct ColorAccess +{ +}; + +template <> +struct ColorAccess<16> +{ + static uint32_t GetPixelWidth() { return 2; } + static uint32_t GetColor(const char8_t *src) + { + return (uint32_t) * reinterpret_cast<const QT3DSU16 *>(src); + } +}; + +template <> +struct ColorAccess<24> +{ + static uint32_t GetPixelWidth() { return 3; } + static uint32_t GetColor(const char8_t *src) + { + return (uint32_t)(*reinterpret_cast<const QT3DSU32 *>(src) & 0xFFFFFF); + } +}; + +template <> +struct ColorAccess<32> +{ + static uint32_t GetPixelWidth() { return 4; } + static uint32_t GetColor(const char8_t *src) + { + return *reinterpret_cast<const uint32_t *>(src); + } +}; + +template <QT3DSU32 TBitCount> +inline void LoadMasked(bool inFlipY, QT3DSI32 *inMasks, void *data, uint8_t *newData, int width, + int height) +{ + const char8_t *oldData = (const char8_t *)data; + SMaskData rMask(inMasks[0]); + SMaskData gMask(inMasks[1]); + SMaskData bMask(inMasks[2]); + for (int h = 0; h < height; ++h) { + int relHeight = h; + if (inFlipY) + relHeight = height - h - 1; + for (int w = 0; w < width; ++w) { + int pos = width * relHeight + w; + const char8_t *readPtr = oldData + (pos * ColorAccess<TBitCount>::GetPixelWidth()); + uint8_t *writePtr = newData + (pos * 3); + uint32_t colorVal = ColorAccess<TBitCount>::GetColor(readPtr); + writePtr[0] = rMask.MapColor(colorVal); + writePtr[1] = gMask.MapColor(colorVal); + writePtr[2] = bMask.MapColor(colorVal); + } + } +} + +void SLoadedTexture::FreeImagePostProcess(bool inFlipY) +{ + // We always convert 32 bit RGBA + if (m_ExtendedFormat != ExtendedTextureFormats::NoExtendedFormat) { + + QT3DSU32 stride = 3 * width; + format = NVRenderTextureFormats::RGB8; + components = 3; + if (m_ExtendedFormat == ExtendedTextureFormats::Palettized && m_TransparentPaletteIndex > -1 + && m_TransparentPaletteIndex < 256) { + stride = 4 * width; + components = 4; + format = NVRenderTextureFormats::RGBA8; + } + QT3DSU32 byteSize = height * stride; + uint8_t *newData = + (uint8_t *)m_Allocator.allocate(byteSize, "texture data", __FILE__, __LINE__); + if (format == NVRenderTextureFormats::RGBA8) + memSet(newData, 255, byteSize); + switch (m_ExtendedFormat) { + case ExtendedTextureFormats::Palettized: { + RGBQUAD *palette = (RGBQUAD *)m_Palette; + switch (m_BitCount) { + case 1: + LoadPalettized<1>(inFlipY, palette, data, newData, width, height, components, + m_TransparentPaletteIndex); + break; + case 2: + LoadPalettized<2>(inFlipY, palette, data, newData, width, height, components, + m_TransparentPaletteIndex); + break; + case 4: + LoadPalettized<4>(inFlipY, palette, data, newData, width, height, components, + m_TransparentPaletteIndex); + break; + case 8: + LoadPalettized<8>(inFlipY, palette, data, newData, width, height, components, + m_TransparentPaletteIndex); + break; + default: + QT3DS_ASSERT(false); + memSet(newData, 0, byteSize); + break; + } + } break; + case ExtendedTextureFormats::CustomRGB: { + switch (m_BitCount) { + case 16: + LoadMasked<16>(inFlipY, m_CustomMasks, data, newData, width, height); + break; + case 24: + LoadMasked<24>(inFlipY, m_CustomMasks, data, newData, width, height); + break; + case 32: + LoadMasked<32>(inFlipY, m_CustomMasks, data, newData, width, height); + break; + default: + QT3DS_ASSERT(false); + memSet(newData, 0, byteSize); + break; + } + } break; + default: + QT3DS_ASSERT(false); + memSet(newData, 0, byteSize); + break; + } + m_Allocator.deallocate(data); + if (m_Palette) + m_Allocator.deallocate(m_Palette); + data = newData; + m_Palette = NULL; + m_BitCount = 0; + this->dataSizeInBytes = byteSize; + m_ExtendedFormat = ExtendedTextureFormats::NoExtendedFormat; + } +} + +SLoadedTexture *SLoadedTexture::LoadBMP(ISeekableIOStream &inStream, bool inFlipY, + NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) +{ + Q_UNUSED(renderContextType) + FreeImageIO theIO(inFnd.getAllocator(), inFnd); + SLoadedTexture *retval = DoLoadBMP(&theIO, &inStream, 0); + if (retval) + retval->FreeImagePostProcess(inFlipY); + return retval; +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp new file mode 100644 index 0000000..19d41d1 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp @@ -0,0 +1,695 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2016 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderLoadedTextureDDS.h" +#include "Qt3DSRenderLoadedTextureFreeImageCompat.h" + +using namespace qt3ds::render; + +namespace qt3ds { +namespace render { + + static int s_exception_string; + + //================================================================================ + // DXT data-layout structure definitions. + typedef struct + { + QT3DSU16 col0; // 16-bit 565 interpolant endpoints + QT3DSU16 col1; + QT3DSU8 row[4]; // 4x4 * 2bpp color-index == 4 bytes. + } DXTColBlock; + + typedef struct + { + QT3DSU16 row[4]; // 4x4 * 4bpp alpha == 8 bytes. (pure 4-bit alpha values) + } DXT3AlphaBlock; + + typedef struct + { + QT3DSU8 alpha0; // 8-bit alpha interpolant endpoints + QT3DSU8 alpha1; + QT3DSU8 row[6]; // 4x4 * 3bpp alpha-index == 48bits == 6 bytes. + } DXT5AlphaBlock; + + typedef struct + { + QT3DSU8 red; + QT3DSU8 green; + QT3DSU8 blue; + QT3DSU8 alpha; + } Color8888; + +//================================================================================ +// Various DDS file defines + +#define DDSD_CAPS 0x00000001l +#define DDSD_HEIGHT 0x00000002l +#define DDSD_WIDTH 0x00000004l +#define DDSD_PIXELFORMAT 0x00001000l +#define DDS_ALPHAPIXELS 0x00000001l +#define DDS_FOURCC 0x00000004l +#define DDS_PITCH 0x00000008l +#define DDS_COMPLEX 0x00000008l +#define DDS_RGB 0x00000040l +#define DDS_TEXTURE 0x00001000l +#define DDS_MIPMAPCOUNT 0x00020000l +#define DDS_LINEARSIZE 0x00080000l +#define DDS_VOLUME 0x00200000l +#define DDS_MIPMAP 0x00400000l +#define DDS_DEPTH 0x00800000l + +#define DDS_CUBEMAP 0x00000200L +#define DDS_CUBEMAP_POSITIVEX 0x00000400L +#define DDS_CUBEMAP_NEGATIVEX 0x00000800L +#define DDS_CUBEMAP_POSITIVEY 0x00001000L +#define DDS_CUBEMAP_NEGATIVEY 0x00002000L +#define DDS_CUBEMAP_POSITIVEZ 0x00004000L +#define DDS_CUBEMAP_NEGATIVEZ 0x00008000L + +#define FOURCC_DXT1 0x31545844 //(MAKEFOURCC('D','X','T','1')) +#define FOURCC_DXT3 0x33545844 //(MAKEFOURCC('D','X','T','3')) +#define FOURCC_DXT5 0x35545844 //(MAKEFOURCC('D','X','T','5')) + +#define DDS_MAGIC_FLIPPED 0x0F7166ED + + //================================================================================ + // DDS file format structures. + typedef struct _DDS_PIXELFORMAT + { + QT3DSU32 dwSize; + QT3DSU32 dwFlags; + QT3DSU32 dwFourCC; + QT3DSU32 dwRGBBitCount; + QT3DSU32 dwRBitMask; + QT3DSU32 dwGBitMask; + QT3DSU32 dwBBitMask; + QT3DSU32 dwABitMask; + } DDS_PIXELFORMAT; + + typedef struct _DDS_HEADER + { + QT3DSU32 dwSize; + QT3DSU32 dwFlags; + QT3DSU32 dwHeight; + QT3DSU32 dwWidth; + QT3DSU32 dwPitchOrLinearSize; + QT3DSU32 dwDepth; + QT3DSU32 dwMipMapCount; + QT3DSU32 dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + QT3DSU32 dwCaps1; + QT3DSU32 dwCaps2; + QT3DSU32 dwReserved2[3]; + } DDS_HEADER; + + //================================================================================ + // helper functions + //================================================================================ + + // helper macros. + static inline void NvSwapChar(QT3DSU8 &a, QT3DSU8 &b) + { + QT3DSU8 tmp; + tmp = a; + a = b; + b = tmp; + } + + static inline void NvSwapShort(QT3DSU16 &a, QT3DSU16 &b) + { + QT3DSU16 tmp; + tmp = a; + a = b; + b = tmp; + } + + //================================================================================ + //================================================================================ + static void flip_blocks_dxtc1(DXTColBlock *line, QT3DSI32 numBlocks) + { + DXTColBlock *curblock = line; + QT3DSI32 i; + + for (i = 0; i < numBlocks; i++) { + NvSwapChar(curblock->row[0], curblock->row[3]); + NvSwapChar(curblock->row[1], curblock->row[2]); + curblock++; + } + } + + //================================================================================ + //================================================================================ + static void flip_blocks_dxtc3(DXTColBlock *line, QT3DSI32 numBlocks) + { + DXTColBlock *curblock = line; + DXT3AlphaBlock *alphablock; + QT3DSI32 i; + + for (i = 0; i < numBlocks; i++) { + alphablock = (DXT3AlphaBlock *)curblock; + + NvSwapShort(alphablock->row[0], alphablock->row[3]); + NvSwapShort(alphablock->row[1], alphablock->row[2]); + curblock++; + + NvSwapChar(curblock->row[0], curblock->row[3]); + NvSwapChar(curblock->row[1], curblock->row[2]); + curblock++; + } + } + + static void flip_dxt5_alpha(DXT5AlphaBlock *block) + { + QT3DSI8 gBits[4][4]; + + const QT3DSU32 mask = 0x00000007; // bits = 00 00 01 11 + QT3DSU32 bits = 0; + memcpy(&bits, &block->row[0], sizeof(QT3DSI8) * 3); + + gBits[0][0] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[0][1] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[0][2] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[0][3] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[1][0] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[1][1] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[1][2] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[1][3] = (QT3DSI8)(bits & mask); + + bits = 0; + memcpy(&bits, &block->row[3], sizeof(QT3DSI8) * 3); + + gBits[2][0] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[2][1] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[2][2] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[2][3] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[3][0] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[3][1] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[3][2] = (QT3DSI8)(bits & mask); + bits >>= 3; + gBits[3][3] = (QT3DSI8)(bits & mask); + + bits = (gBits[3][0] << 0) | (gBits[3][1] << 3) | (gBits[3][2] << 6) | (gBits[3][3] << 9) + | (gBits[2][0] << 12) | (gBits[2][1] << 15) | (gBits[2][2] << 18) | (gBits[2][3] << 21); + memcpy(&block->row[0], &bits, 3); + + bits = (gBits[1][0] << 0) | (gBits[1][1] << 3) | (gBits[1][2] << 6) | (gBits[1][3] << 9) + | (gBits[0][0] << 12) | (gBits[0][1] << 15) | (gBits[0][2] << 18) | (gBits[0][3] << 21); + memcpy(&block->row[3], &bits, 3); + } + + static void flip_blocks_dxtc5(DXTColBlock *line, QT3DSI32 numBlocks) + { + DXTColBlock *curblock = line; + DXT5AlphaBlock *alphablock; + QT3DSI32 i; + + for (i = 0; i < numBlocks; i++) { + alphablock = (DXT5AlphaBlock *)curblock; + + flip_dxt5_alpha(alphablock); + curblock++; + + NvSwapChar(curblock->row[0], curblock->row[3]); + NvSwapChar(curblock->row[1], curblock->row[2]); + curblock++; + } + } + + static void flip_data_vertical(FreeImageIO *io, QT3DSI8 *image, QT3DSI32 width, QT3DSI32 height, + Qt3DSDDSImage *info) + { + if (info->compressed) { + QT3DSI32 linesize, j; + DXTColBlock *top; + DXTColBlock *bottom; + QT3DSI8 *tmp; + void (*flipblocks)(DXTColBlock *, QT3DSI32) = NULL; + QT3DSI32 xblocks = width / 4; + QT3DSI32 yblocks = height / 4; + QT3DSI32 blocksize; + + switch (info->format) { + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT1: + blocksize = 8; + flipblocks = &flip_blocks_dxtc1; + break; + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT3: + blocksize = 16; + flipblocks = &flip_blocks_dxtc3; + break; + case qt3ds::render::NVRenderTextureFormats::RGBA_DXT5: + blocksize = 16; + flipblocks = &flip_blocks_dxtc5; + break; + default: + return; + } + + linesize = xblocks * blocksize; + tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical compressed"); + + for (j = 0; j < (yblocks >> 1); j++) { + top = (DXTColBlock *)(void *)(image + j * linesize); + bottom = (DXTColBlock *)(void *)(image + (((yblocks - j) - 1) * linesize)); + + (*flipblocks)(top, xblocks); + (*flipblocks)(bottom, xblocks); + + memcpy(tmp, bottom, linesize); + memcpy(bottom, top, linesize); + memcpy(top, tmp, linesize); + } + + // Catch the middle row of blocks if there is one + // The loop above will skip the middle row + if (yblocks & 0x01) { + DXTColBlock *middle = (DXTColBlock *)(void *)(image + (yblocks >> 1) * linesize); + (*flipblocks)(middle, xblocks); + } + + QT3DS_FREE(io->m_Allocator, tmp); + } else { + QT3DSI32 linesize = width * info->bytesPerPixel; + QT3DSI32 j; + QT3DSI8 *top; + QT3DSI8 *bottom; + QT3DSI8 *tmp; + + // much simpler - just compute the line length and swap each row + tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical"); + ; + + for (j = 0; j < (height >> 1); j++) { + top = (QT3DSI8 *)(image + j * linesize); + bottom = (QT3DSI8 *)(image + (((height - j) - 1) * linesize)); + + memcpy(tmp, bottom, linesize); + memcpy(bottom, top, linesize); + memcpy(top, tmp, linesize); + } + + QT3DS_FREE(io->m_Allocator, tmp); + } + } + + static QT3DSI32 size_image(QT3DSI32 width, QT3DSI32 height, const Qt3DSDDSImage *image) + { + if (image->compressed) { + return ((width + 3) / 4) * ((height + 3) / 4) + * (image->format == qt3ds::render::NVRenderTextureFormats::RGBA_DXT1 ? 8 : 16); + } else { + return width * height * image->bytesPerPixel; + } + } + + static QT3DSI32 total_image_data_size(Qt3DSDDSImage *image) + { + QT3DSI32 i, j, index = 0, size = 0, w, h; + QT3DSI32 cubeCount = image->cubemap ? 6 : 1; + + for (j = 0; j < cubeCount; j++) { + w = image->width; + h = image->height; + + for (i = 0; i < image->numMipmaps; i++) // account for base plus each mip + { + image->size[index] = size_image(w, h, image); + image->mipwidth[index] = w; + image->mipheight[index] = h; + size += image->size[index]; + if (w != 1) { + w >>= 1; + } + if (h != 1) { + h >>= 1; + } + + index++; + } + } + + return (size); + } + + void *Qt3DSDDSAllocDataBlock(FreeImageIO *io, Qt3DSDDSImage *image) + { + if (image) { + QT3DSI32 i; + QT3DSI32 size = total_image_data_size(image); + image->dataBlock = + QT3DS_ALLOC(io->m_Allocator, size, + "Qt3DSDDSAllocDataBlock"); // no need to calloc, as we fill every bit... + if (image->dataBlock == NULL) { + return NULL; + } + + image->data[0] = image->dataBlock; + + QT3DSI32 planes = image->numMipmaps * (image->cubemap ? 6 : 1); + + for (i = 1; i < planes; i++) // account for base plus each mip + { + image->data[i] = + (void *)(((size_t)(image->data[i - 1])) + (size_t)image->size[i - 1]); + } + + return (image->dataBlock); // in case caller wants to sanity check... + } + return (NULL); + } + + static FIBITMAP *DoLoadDDS(FreeImageIO *io, IInStream &inStream, QT3DSI32 flipVertical) + { + FIBITMAP *dib = NULL; + DDS_HEADER ddsh; + QT3DSI8 filecode[4]; + Qt3DSDDSImage *image = NULL; + bool needsBGRASwap = false; + ; + bool isAllreadyFlipped = false; + + try { + // check file code + inStream.Read(filecode, 4); + if (memcmp(filecode, "DDS ", 4)) { + throw "Invalid DDS file"; + } + + image = (Qt3DSDDSImage *)QT3DS_ALLOC(io->m_Allocator, sizeof(Qt3DSDDSImage), "DoLoadDDS"); + if (image == NULL) { + throw "Qt3DSDDSImage allocation failed"; + } + memset(image, 0, sizeof(Qt3DSDDSImage)); + + // read in DDS header + inStream.Read(&ddsh, 1); + + // check if image is a cubempap + if (ddsh.dwCaps2 & DDS_CUBEMAP) { + const QT3DSI32 allFaces = DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_POSITIVEY + | DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEX | DDS_CUBEMAP_NEGATIVEY + | DDS_CUBEMAP_NEGATIVEZ; + + if ((ddsh.dwCaps2 & allFaces) != allFaces) { + throw "Not all cubemap faces defined - not supported"; + } + + image->cubemap = 1; + } else { + image->cubemap = 0; + } + + // check if image is a volume texture + if ((ddsh.dwCaps2 & DDS_VOLUME) && (ddsh.dwDepth > 0)) { + throw "Volume textures not supported"; + } + + // allocated the memory for the structure we return + dib = QT3DS_NEW(io->m_Allocator, SLoadedTexture)(io->m_Allocator); + if (dib == NULL) { + throw "DIB allocation failed"; + } + + // figure out what the image format is + if (ddsh.ddspf.dwFlags & DDS_FOURCC) { + switch (ddsh.ddspf.dwFourCC) { + case FOURCC_DXT1: + image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT1; + image->components = 3; + image->compressed = 1; + image->alpha = 0; // Ugh - for backwards compatibility + dib->format = qt3ds::render::NVRenderTextureFormats::RGB_DXT1; + break; + case FOURCC_DXT3: + image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3; + image->components = 4; + image->compressed = 1; + image->alpha = 1; + dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3; + break; + case FOURCC_DXT5: + image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5; + image->components = 4; + image->compressed = 1; + image->alpha = 1; + dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5; + break; + default: + throw "Unsupported FOURCC code"; + } + } else { + // Check for a supported pixel format + if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x000000FF) + && (ddsh.ddspf.dwGBitMask == 0x0000FF00) + && (ddsh.ddspf.dwBBitMask == 0x00FF0000) + && (ddsh.ddspf.dwABitMask == 0xFF000000)) { + // We support D3D's A8B8G8R8, which is actually RGBA in linear + // memory, equivalent to GL's RGBA + image->format = qt3ds::render::NVRenderTextureFormats::RGBA8; + image->components = 4; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8; + image->bytesPerPixel = 4; + image->alpha = 1; + image->compressed = 0; + dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8; + } else if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x00FF0000) + && (ddsh.ddspf.dwGBitMask == 0x0000FF00) + && (ddsh.ddspf.dwBBitMask == 0x000000FF) + && (ddsh.ddspf.dwABitMask == 0xFF000000)) { + // We support D3D's A8R8G8B8, which is actually BGRA in linear + // memory, need to be + image->format = qt3ds::render::NVRenderTextureFormats::RGBA8; + image->components = 4; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8; + image->bytesPerPixel = 4; + image->alpha = 1; + image->compressed = 0; + needsBGRASwap = true; + dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8; + } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x0000F800) + && (ddsh.ddspf.dwGBitMask == 0x000007E0) + && (ddsh.ddspf.dwBBitMask == 0x0000001F) + && (ddsh.ddspf.dwABitMask == 0x00000000)) { + // We support D3D's R5G6B5, which is actually RGB in linear + // memory. It is equivalent to GL's GL_UNSIGNED_SHORT_5_6_5 + image->format = qt3ds::render::NVRenderTextureFormats::RGB8; + image->components = 3; + image->alpha = 0; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU16; + image->bytesPerPixel = 2; + image->compressed = 0; + dib->format = qt3ds::render::NVRenderTextureFormats::RGB8; + } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x00000000) + && (ddsh.ddspf.dwGBitMask == 0x00000000) + && (ddsh.ddspf.dwBBitMask == 0x00000000) + && (ddsh.ddspf.dwABitMask == 0x000000FF)) { + // We support D3D's A8 + image->format = qt3ds::render::NVRenderTextureFormats::Alpha8; + image->components = 1; + image->alpha = 1; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8; + image->bytesPerPixel = 1; + image->compressed = 0; + dib->format = qt3ds::render::NVRenderTextureFormats::Alpha8; + } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x000000FF) + && (ddsh.ddspf.dwGBitMask == 0x00000000) + && (ddsh.ddspf.dwBBitMask == 0x00000000) + && (ddsh.ddspf.dwABitMask == 0x00000000)) { + // We support D3D's L8 (flagged as 8 bits of red only) + image->format = qt3ds::render::NVRenderTextureFormats::Luminance8; + image->components = 1; + image->alpha = 0; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8; + image->bytesPerPixel = 1; + image->compressed = 0; + dib->format = qt3ds::render::NVRenderTextureFormats::Luminance8; + } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x000000FF) + && (ddsh.ddspf.dwGBitMask == 0x00000000) + && (ddsh.ddspf.dwBBitMask == 0x00000000) + && (ddsh.ddspf.dwABitMask == 0x0000FF00)) { + // We support D3D's A8L8 (flagged as 8 bits of red and 8 bits of alpha) + image->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8; + image->components = 2; + image->alpha = 1; + image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8; + image->bytesPerPixel = 2; + image->compressed = 0; + dib->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8; + } else { + throw "not a DXTC or supported RGB(A) format image"; + } + } + + // detect flagging to indicate this texture was stored in a y-inverted fashion + if (!(ddsh.dwFlags & DDS_LINEARSIZE)) { + if (ddsh.dwPitchOrLinearSize == DDS_MAGIC_FLIPPED) { + isAllreadyFlipped = true; + } + } + + flipVertical = (isAllreadyFlipped != (flipVertical ? true : false)) ? 1 : 0; + + // store primary surface width/height/numMipmaps + image->width = ddsh.dwWidth; + image->height = ddsh.dwHeight; + image->numMipmaps = ddsh.dwFlags & DDS_MIPMAPCOUNT ? ddsh.dwMipMapCount : 1; + + if (image->numMipmaps > QT3DS_DDS_MAX_MIPMAPS) { + throw "Too many mipmaps: max 16"; + } + + // allocate the meta datablock for all mip storage. + Qt3DSDDSAllocDataBlock(io, image); + if (image->dataBlock == NULL) { + throw "Failed to allocate memory for image data storage"; + } + + dib->width = image->width; + dib->height = image->height; + dib->dds = image; + + QT3DSI32 faces = image->cubemap ? 6 : 1; + + QT3DSI32 index = 0; + for (QT3DSI32 j = 0; j < faces; j++) { + // load all surfaces for the image + QT3DSI32 width = image->width; + QT3DSI32 height = image->height; + + for (QT3DSI32 i = 0; i < image->numMipmaps; i++) { + // Get the size, read in the data. + inStream.Read( + NVDataRef<QT3DSU8>((QT3DSU8 *)image->data[index], (QT3DSU32)image->size[index])); + + // Flip in Y for OpenGL if needed + if (flipVertical) + flip_data_vertical(io, (QT3DSI8 *)image->data[index], width, height, image); + + // shrink to next power of 2 + width >>= 1; + height >>= 1; + + if (!width) + width = 1; + + if (!height) + height = 1; + + // make sure DXT isn't <4 on a side... + if (image->compressed) { + if (width < 4) + width = 4; + if (height < 4) + height = 4; + } + + index++; + } + } + + if (needsBGRASwap) { + QT3DSI32 index = 0; + QT3DSI32 k; + + for (k = 0; k < faces; k++) { + QT3DSI32 width = image->width; + QT3DSI32 height = image->height; + + for (QT3DSI32 i = 0; i < image->numMipmaps; i++) { + QT3DSI8 *data = (QT3DSI8 *)(image->data[index]); + QT3DSI32 pixels = width * height; + QT3DSI32 j; + + for (j = 0; j < pixels; j++) { + QT3DSI8 temp = data[0]; + data[0] = data[2]; + data[2] = temp; + + data += 4; + } + + // shrink to next power of 2 + width >>= 1; + height >>= 1; + + if (!width) + width = 1; + + if (!height) + height = 1; + + index++; + } + } + } + } catch (const char *message) { + if (image) { + if (image->dataBlock) + QT3DS_FREE(io->m_Allocator, image->dataBlock); + + QT3DS_FREE(io->m_Allocator, image); + } + if (dib) { + FreeImage_Unload(dib); + } + if (message) { + FreeImage_OutputMessageProc(s_exception_string, message, io); + } + } + + return dib; + } + + SLoadedTexture *SLoadedTexture::LoadDDS(IInStream &inStream, QT3DSI32 flipVertical, + NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) + { + Q_UNUSED(renderContextType) + FreeImageIO theIO(inFnd.getAllocator(), inFnd); + SLoadedTexture *retval = DoLoadDDS(&theIO, inStream, flipVertical); + + return retval; + } +} +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h new file mode 100644 index 0000000..8490b47 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2016 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ + +#pragma once +#ifndef QT3DS_RENDER_LOAD_DDS_H +#define QT3DS_RENDER_LOAD_DDS_H + +namespace qt3ds { +namespace render { + +/** The maximum number of mipmap levels (per texture or per cubemap face) */ +#define QT3DS_DDS_MAX_MIPMAPS (16) + +/** The number of cubemap faces that must exist in a cubemap-bearing DDS file */ +#define QT3DS_DDS_NUM_CUBEMAP_FACES (6) + + /** The master DDS structure for loading and saving + + This is the master DDS structure. It shouldn't be allocated by hand, + always use NVHHDDSAlloc/NVHHDDSAllocData/NVHHDDSFree to manage them properly. + */ + + struct Qt3DSDDSImage + { + /** Width of the overall texture in texels */ + int width; + /** Height of the overall texture in texels */ + int height; + /** Number of color/alpha components per texel 1-4 */ + int components; + /** The GL type of each color component (noncompressed textures only) */ + int componentFormat; + /** The number of bytes per pixel (noncompressed textures only) */ + int bytesPerPixel; + /** Nonzero if the format is DXT-compressed */ + int compressed; + /** The number of levels in the mipmap pyramid (including the base level) */ + int numMipmaps; + /** If nonzero, then the file contains 6 cubemap faces */ + int cubemap; + /** The format of the loaded texture data */ + int format; + /** The GL internal format of the loaded texture data(compressed textures only) */ + int internalFormat; + /** Nonzero if the texture data includes alpha */ + int alpha; + /** Base of the allocated block of all texel data */ + void *dataBlock; + /** Pointers to the mipmap levels for the texture or each cubemap face */ + void *data[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES]; + /** Array of sizes of the mipmap levels for the texture or each cubemap face */ + int size[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES]; + /** Array of widths of the mipmap levels for the texture or each cubemap face */ + int mipwidth[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES]; + /** Array of heights of the mipmap levels for the texture or each cubemap face */ + int mipheight[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES]; + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h new file mode 100644 index 0000000..13f317b --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_LOADED_TEXTURE_FREEIMAGE_COMPAT_H +#define QT3DS_RENDER_LOADED_TEXTURE_FREEIMAGE_COMPAT_H +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSRenderLoadedTexture.h" +#include "EASTL/algorithm.h" +#include <stdlib.h> +#ifndef _MACOSX +#ifndef _INTEGRITYPLATFORM +#include <malloc.h> +#endif +#endif + +// We use a compatibility layer so we can easily convert freeimage code to load our texture formats +// where necessary. + +namespace qt3ds { +namespace render { + using namespace qt3ds::foundation; + + typedef int32_t BOOL; + typedef uint8_t BYTE; + typedef uint16_t WORD; + typedef uint32_t DWORD; + typedef int32_t LONG; + +#define FREEIMAGE_COLORORDER_BGR 0 +#define FREEIMAGE_COLORORDER_RGB 1 + +#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR + + typedef ISeekableIOStream *fi_handle; + + struct FreeImageIO + { + NVAllocatorCallback &m_Allocator; + NVFoundationBase &m_Foundation; + int (*read_proc)(void *data, int size, int itemSize, fi_handle handle); + void (*seek_proc)(fi_handle handle, int offset, int pos); + int (*tell_proc)(fi_handle handle); + static inline int reader(void *data, int size, int itemSize, fi_handle handle) + { + NVDataRef<QT3DSU8> theData(toDataRef((QT3DSU8 *)data, (QT3DSU32)size * itemSize)); + QT3DSU32 amount = handle->Read(theData); + return (int)amount; + } + static inline void seeker(fi_handle handle, int offset, int pos) + { + SeekPosition::Enum seekPos(SeekPosition::Begin); + /* +#define SEEK_CUR 1 +#define SEEK_END 2 +#define SEEK_SET 0*/ + switch (pos) { + case 0: + seekPos = SeekPosition::Begin; + break; + case 1: + seekPos = SeekPosition::Current; + break; + case 2: + seekPos = SeekPosition::End; + break; + default: + QT3DS_ASSERT(false); + break; + } + handle->SetPosition(offset, seekPos); + } + static inline int teller(fi_handle handle) { return (int)handle->GetPosition(); } + FreeImageIO(NVAllocatorCallback &alloc, NVFoundationBase &fnd) + : m_Allocator(alloc) + , m_Foundation(fnd) + , read_proc(reader) + , seek_proc(seeker) + , tell_proc(teller) + { + } + }; + + typedef SLoadedTexture FIBITMAP; + inline BYTE *FreeImage_GetBits(FIBITMAP *bmp) { return (BYTE *)bmp->data; } + + inline int FreeImage_GetHeight(FIBITMAP *bmp) { return bmp->height; } + inline int FreeImage_GetWidth(FIBITMAP *bmp) { return bmp->width; } + +#define INPLACESWAP(x, y) eastl::swap(x, y) +#define MIN(x, y) NVMin(x, y) +#define MAX(x, y) NVMax(x, y) + +#define TRUE 1 +#define FALSE 0 + + typedef struct tagBITMAPINFOHEADER + { + DWORD biSize; + LONG biWidth; + LONG biHeight; + WORD biPlanes; + WORD biBitCount; + DWORD biCompression; + DWORD biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + DWORD biClrUsed; + DWORD biClrImportant; + } BITMAPINFOHEADER, *PBITMAPINFOHEADER; + + typedef struct tagRGBQUAD + { +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; +#else + BYTE rgbRed; + BYTE rgbGreen; + BYTE rgbBlue; +#endif // FREEIMAGE_COLORORDER + BYTE rgbReserved; + } RGBQUAD; + + typedef struct tagRGBTRIPLE + { +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + BYTE rgbtBlue; + BYTE rgbtGreen; + BYTE rgbtRed; +#else + BYTE rgbtRed; + BYTE rgbtGreen; + BYTE rgbtBlue; +#endif // FREEIMAGE_COLORORDER + } RGBTRIPLE; + + typedef struct tagBITMAPINFO + { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[1]; + } BITMAPINFO, *PBITMAPINFO; + + typedef struct tagFILE_RGBA + { + unsigned char r, g, b, a; + } FILE_RGBA; + + typedef struct tagFILE_BGRA + { + unsigned char b, g, r, a; + } FILE_BGRA; + + typedef struct tagFILE_RGB + { + unsigned char r, g, b; + } FILE_RGB; + + typedef struct tagFILE_BGR + { + unsigned char b, g, r; + } FILE_BGR; + +// Indexes for byte arrays, masks and shifts for treating pixels as words --- +// These coincide with the order of RGBQUAD and RGBTRIPLE ------------------- + +#ifndef FREEIMAGE_BIGENDIAN +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR +// Little Endian (x86 / MS Windows, Linux) : BGR(A) order +#define FI_RGBA_RED 2 +#define FI_RGBA_GREEN 1 +#define FI_RGBA_BLUE 0 +#define FI_RGBA_ALPHA 3 +#define FI_RGBA_RED_MASK 0x00FF0000 +#define FI_RGBA_GREEN_MASK 0x0000FF00 +#define FI_RGBA_BLUE_MASK 0x000000FF +#define FI_RGBA_ALPHA_MASK 0xFF000000 +#define FI_RGBA_RED_SHIFT 16 +#define FI_RGBA_GREEN_SHIFT 8 +#define FI_RGBA_BLUE_SHIFT 0 +#define FI_RGBA_ALPHA_SHIFT 24 +#else +// Little Endian (x86 / MaxOSX) : RGB(A) order +#define FI_RGBA_RED 0 +#define FI_RGBA_GREEN 1 +#define FI_RGBA_BLUE 2 +#define FI_RGBA_ALPHA 3 +#define FI_RGBA_RED_MASK 0x000000FF +#define FI_RGBA_GREEN_MASK 0x0000FF00 +#define FI_RGBA_BLUE_MASK 0x00FF0000 +#define FI_RGBA_ALPHA_MASK 0xFF000000 +#define FI_RGBA_RED_SHIFT 0 +#define FI_RGBA_GREEN_SHIFT 8 +#define FI_RGBA_BLUE_SHIFT 16 +#define FI_RGBA_ALPHA_SHIFT 24 +#endif // FREEIMAGE_COLORORDER +#else +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR +// Big Endian (PPC / none) : BGR(A) order +#define FI_RGBA_RED 2 +#define FI_RGBA_GREEN 1 +#define FI_RGBA_BLUE 0 +#define FI_RGBA_ALPHA 3 +#define FI_RGBA_RED_MASK 0x0000FF00 +#define FI_RGBA_GREEN_MASK 0x00FF0000 +#define FI_RGBA_BLUE_MASK 0xFF000000 +#define FI_RGBA_ALPHA_MASK 0x000000FF +#define FI_RGBA_RED_SHIFT 8 +#define FI_RGBA_GREEN_SHIFT 16 +#define FI_RGBA_BLUE_SHIFT 24 +#define FI_RGBA_ALPHA_SHIFT 0 +#else +// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order +#define FI_RGBA_RED 0 +#define FI_RGBA_GREEN 1 +#define FI_RGBA_BLUE 2 +#define FI_RGBA_ALPHA 3 +#define FI_RGBA_RED_MASK 0xFF000000 +#define FI_RGBA_GREEN_MASK 0x00FF0000 +#define FI_RGBA_BLUE_MASK 0x0000FF00 +#define FI_RGBA_ALPHA_MASK 0x000000FF +#define FI_RGBA_RED_SHIFT 24 +#define FI_RGBA_GREEN_SHIFT 16 +#define FI_RGBA_BLUE_SHIFT 8 +#define FI_RGBA_ALPHA_SHIFT 0 +#endif // FREEIMAGE_COLORORDER +#endif // FREEIMAGE_BIGENDIAN + +#define FI_RGBA_RGB_MASK (FI_RGBA_RED_MASK | FI_RGBA_GREEN_MASK | FI_RGBA_BLUE_MASK) + +// The 16bit macros only include masks and shifts, since each color element is not byte aligned + +#define FI16_555_RED_MASK 0x7C00 +#define FI16_555_GREEN_MASK 0x03E0 +#define FI16_555_BLUE_MASK 0x001F +#define FI16_555_RED_SHIFT 10 +#define FI16_555_GREEN_SHIFT 5 +#define FI16_555_BLUE_SHIFT 0 +#define FI16_565_RED_MASK 0xF800 +#define FI16_565_GREEN_MASK 0x07E0 +#define FI16_565_BLUE_MASK 0x001F +#define FI16_565_RED_SHIFT 11 +#define FI16_565_GREEN_SHIFT 5 +#define FI16_565_BLUE_SHIFT 0 + + inline unsigned char HINIBBLE(unsigned char byte) { return byte & 0xF0; } + + inline unsigned char LOWNIBBLE(unsigned char byte) { return byte & 0x0F; } + + inline int CalculateUsedBits(int bits) + { + int bit_count = 0; + unsigned bit = 1; + + for (unsigned i = 0; i < 32; i++) { + if ((bits & bit) == bit) { + bit_count++; + } + + bit <<= 1; + } + + return bit_count; + } + + inline int CalculateLine(int width, int bitdepth) { return ((width * bitdepth) + 7) / 8; } + + inline int CalculatePitch(int line) { return (line + 3) & ~3; } + + inline int CalculateUsedPaletteEntries(int bit_count) + { + if ((bit_count >= 1) && (bit_count <= 8)) + return 1 << bit_count; + + return 0; + } + + inline unsigned char *CalculateScanLine(unsigned char *bits, unsigned pitch, int scanline) + { + return (bits + (pitch * scanline)); + } + + inline void ReplaceExtension(char *result, const char *filename, const char *extension) + { + for (size_t i = strlen(filename) - 1; i > 0; --i) { + if (filename[i] == '.') { + memcpy(result, filename, i); + result[i] = '.'; + memcpy(result + i + 1, extension, strlen(extension) + 1); + return; + } + } + + memcpy(result, filename, strlen(filename)); + result[strlen(filename)] = '.'; + memcpy(result + strlen(filename) + 1, extension, strlen(extension) + 1); + } + + inline BYTE *FreeImage_GetScanLine(FIBITMAP *bmp, int height) + { + return CalculateScanLine( + (BYTE *)bmp->data, CalculatePitch(CalculateLine(bmp->width, bmp->m_BitCount)), height); + } + +#define DLL_CALLCONV + +// ignored for now. +#define FreeImage_SetDotsPerMeterX(img, dots) +#define FreeImage_SetDotsPerMeterY(img, dots) + + inline SLoadedTexture *FreeImage_Allocate(int width, int height, int bit_count, FreeImageIO *io) + { + SLoadedTexture *theTexture = QT3DS_NEW(io->m_Allocator, SLoadedTexture)(io->m_Allocator); + int pitch = CalculatePitch(CalculateLine(width, bit_count)); + QT3DSU32 dataSize = (QT3DSU32)(height * pitch); + theTexture->dataSizeInBytes = dataSize; + theTexture->data = io->m_Allocator.allocate(dataSize, "image data", __FILE__, __LINE__); + memZero(theTexture->data, dataSize); + theTexture->width = width; + theTexture->height = height; + theTexture->m_BitCount = bit_count; + // If free image asks us for a palette, we change our format at that time. + theTexture->m_ExtendedFormat = ExtendedTextureFormats::CustomRGB; + return theTexture; + } + + inline SLoadedTexture *FreeImage_Allocate(int width, int height, int bit_count, int rmask, + int gmask, int bmask, FreeImageIO *io) + { + SLoadedTexture *retval = FreeImage_Allocate(width, height, bit_count, io); + retval->m_CustomMasks[0] = rmask; + retval->m_CustomMasks[1] = gmask; + retval->m_CustomMasks[2] = bmask; + return retval; + } + + inline RGBQUAD *FreeImage_GetPalette(SLoadedTexture *texture) + { + if (texture->m_Palette == NULL) { + texture->m_ExtendedFormat = ExtendedTextureFormats::Palettized; + QT3DSU32 memory = 256 * sizeof(RGBQUAD); + if (memory) { + texture->m_Palette = + texture->m_Allocator.allocate(memory, "texture palette", __FILE__, __LINE__); + memZero(texture->m_Palette, memory); + } + } + return (RGBQUAD *)texture->m_Palette; + } + + inline void FreeImage_Unload(SLoadedTexture *texture) { texture->release(); } + inline void FreeImage_OutputMessageProc(int, const char *message, FreeImageIO *io) + { + Q_UNUSED(io); + qCCritical(INVALID_OPERATION, "Error loading image: %s", message); + } + + inline void FreeImage_SetBackgroundColor(SLoadedTexture *texture, RGBQUAD *inColor) + { + if (inColor) { + texture->m_BackgroundColor[0] = inColor->rgbRed; + texture->m_BackgroundColor[1] = inColor->rgbGreen; + texture->m_BackgroundColor[2] = inColor->rgbBlue; + } else + memSet(texture->m_BackgroundColor, 0, 3); + } + + inline void FreeImage_SetTransparencyTable(SLoadedTexture *texture, BYTE *table, int size) + { + if (texture->m_TransparencyTable) + texture->m_Allocator.deallocate(texture->m_TransparencyTable); + texture->m_TransparencyTable = NULL; + if (table && size) { + texture->m_TransparencyTable = (uint8_t *)texture->m_Allocator.allocate( + size, "texture transparency table", __FILE__, __LINE__); + memCopy(texture->m_TransparencyTable, table, size); + } + } +} +} + +using namespace qt3ds::render; + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp new file mode 100644 index 0000000..ece4d3a --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp @@ -0,0 +1,851 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +// ========================================================== +// GIF Loader and Writer +// +// Design and implementation by +// - Ryan Rubley <ryan@lostreality.org> +// - Raphael Gaquer <raphael.gaquer@alcer.com> +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== +#ifdef _MSC_VER +#pragma warning(disable : 4786) // identifier was truncated to 'number' characters +#endif + +#include "Qt3DSRenderLoadedTextureFreeImageCompat.h" +#include "EASTL/vector.h" +#include "EASTL/string.h" +// ========================================================== +// Metadata declarations +// ========================================================== + +#define GIF_DISPOSAL_UNSPECIFIED 0 +#define GIF_DISPOSAL_LEAVE 1 +#define GIF_DISPOSAL_BACKGROUND 2 +#define GIF_DISPOSAL_PREVIOUS 3 + +// ========================================================== +// Constant/Typedef declarations +// ========================================================== + +struct GIFinfo +{ + BOOL read; + // only really used when reading + size_t global_color_table_offset; + int global_color_table_size; + BYTE background_color; + eastl::vector<size_t> application_extension_offsets; + eastl::vector<size_t> comment_extension_offsets; + eastl::vector<size_t> graphic_control_extension_offsets; + eastl::vector<size_t> image_descriptor_offsets; + + GIFinfo() + : read(0) + , global_color_table_offset(0) + , global_color_table_size(0) + , background_color(0) + { + } +}; + +struct PageInfo +{ + PageInfo(int d, int l, int t, int w, int h) + { + disposal_method = d; + left = (WORD)l; + top = (WORD)t; + width = (WORD)w; + height = (WORD)h; + } + int disposal_method; + WORD left, top, width, height; +}; + +// GIF defines a max of 12 bits per code +#define MAX_LZW_CODE 4096 + +class StringTable +{ +public: + StringTable(); + ~StringTable(); + void Initialize(int minCodeSize); + BYTE *FillInputBuffer(int len); + void CompressStart(int bpp, int width); + int CompressEnd(BYTE *buf); // 0-4 bytes + bool Compress(BYTE *buf, int *len); + bool Decompress(BYTE *buf, int *len); + void Done(void); + +protected: + bool m_done; + + int m_minCodeSize, m_clearCode, m_endCode, m_nextCode; + + int m_bpp, m_slack; // Compressor information + + int m_prefix; // Compressor state variable + int m_codeSize, m_codeMask; // Compressor/Decompressor state variables + int m_oldCode; // Decompressor state variable + int m_partial, m_partialSize; // Compressor/Decompressor bit buffer + + int firstPixelPassed; // A specific flag that indicates if the first pixel + // of the whole image had already been read + + eastl::string m_strings[MAX_LZW_CODE]; // This is what is really the "string table" data for the + // Decompressor + int *m_strmap; + + // input buffer + BYTE *m_buffer; + int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift; + + void ClearCompressorTable(void); + void ClearDecompressorTable(void); +}; + +#define GIF_PACKED_LSD_HAVEGCT 0x80 +#define GIF_PACKED_LSD_COLORRES 0x70 +#define GIF_PACKED_LSD_GCTSORTED 0x08 +#define GIF_PACKED_LSD_GCTSIZE 0x07 +#define GIF_PACKED_ID_HAVELCT 0x80 +#define GIF_PACKED_ID_INTERLACED 0x40 +#define GIF_PACKED_ID_LCTSORTED 0x20 +#define GIF_PACKED_ID_RESERVED 0x18 +#define GIF_PACKED_ID_LCTSIZE 0x07 +#define GIF_PACKED_GCE_RESERVED 0xE0 +#define GIF_PACKED_GCE_DISPOSAL 0x1C +#define GIF_PACKED_GCE_WAITINPUT 0x02 +#define GIF_PACKED_GCE_HAVETRANS 0x01 + +#define GIF_BLOCK_IMAGE_DESCRIPTOR 0x2C +#define GIF_BLOCK_EXTENSION 0x21 +#define GIF_BLOCK_TRAILER 0x3B + +#define GIF_EXT_PLAINTEXT 0x01 +#define GIF_EXT_GRAPHIC_CONTROL 0xF9 +#define GIF_EXT_COMMENT 0xFE +#define GIF_EXT_APPLICATION 0xFF + +#define GIF_INTERLACE_PASSES 4 +static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = { 0, 4, 2, 1 }; +static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = { 8, 8, 4, 2 }; + +StringTable::StringTable() +{ + m_buffer = NULL; + firstPixelPassed = 0; // Still no pixel read + // Maximum number of entries in the map is MAX_LZW_CODE * 256 + // (aka 2**12 * 2**8 => a 20 bits key) + // This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp) + m_strmap = (int *)new int[1 << 20]; +} + +StringTable::~StringTable() +{ + if (m_buffer != NULL) { + delete[] m_buffer; + } + if (m_strmap != NULL) { + delete[] m_strmap; + m_strmap = NULL; + } +} + +void StringTable::Initialize(int minCodeSize) +{ + m_done = false; + + m_bpp = 8; + m_minCodeSize = minCodeSize; + m_clearCode = 1 << m_minCodeSize; + if (m_clearCode > MAX_LZW_CODE) { + m_clearCode = MAX_LZW_CODE; + } + m_endCode = m_clearCode + 1; + + m_partial = 0; + m_partialSize = 0; + + m_bufferSize = 0; + ClearCompressorTable(); + ClearDecompressorTable(); +} + +BYTE *StringTable::FillInputBuffer(int len) +{ + if (m_buffer == NULL) { + m_buffer = new BYTE[len]; + m_bufferRealSize = len; + } else if (len > m_bufferRealSize) { + delete[] m_buffer; + m_buffer = new BYTE[len]; + m_bufferRealSize = len; + } + m_bufferSize = len; + m_bufferPos = 0; + m_bufferShift = 8 - m_bpp; + return m_buffer; +} + +void StringTable::CompressStart(int bpp, int width) +{ + m_bpp = bpp; + m_slack = (8 - ((width * bpp) % 8)) % 8; + + m_partial |= m_clearCode << m_partialSize; + m_partialSize += m_codeSize; + ClearCompressorTable(); +} + +int StringTable::CompressEnd(BYTE *buf) +{ + int len = 0; + + // output code for remaining prefix + m_partial |= m_prefix << m_partialSize; + m_partialSize += m_codeSize; + while (m_partialSize >= 8) { + *buf++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + len++; + } + + // add the end of information code and flush the entire buffer out + m_partial |= m_endCode << m_partialSize; + m_partialSize += m_codeSize; + while (m_partialSize > 0) { + *buf++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + len++; + } + + // most this can be is 4 bytes. 7 bits in m_partial to start + 12 for the + // last code + 12 for the end code = 31 bits total. + return len; +} + +bool StringTable::Compress(BYTE *buf, int *len) +{ + if (m_bufferSize == 0 || m_done) { + return false; + } + + int mask = (1 << m_bpp) - 1; + BYTE *bufpos = buf; + while (m_bufferPos < m_bufferSize) { + // get the current pixel value + char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask); + + // The next prefix is : + // <the previous LZW code (on 12 bits << 8)> | <the code of the current pixel (on 8 bits)> + int nextprefix = (((m_prefix) << 8) & 0xFFF00) + (ch & 0x000FF); + if (firstPixelPassed) { + + if (m_strmap[nextprefix] > 0) { + m_prefix = m_strmap[nextprefix]; + } else { + m_partial |= m_prefix << m_partialSize; + m_partialSize += m_codeSize; + // grab full bytes for the output buffer + while (m_partialSize >= 8 && bufpos - buf < *len) { + *bufpos++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + } + + // add the code to the "table map" + m_strmap[nextprefix] = m_nextCode; + + // increment the next highest valid code, increase the code size + if (m_nextCode == (1 << m_codeSize)) { + m_codeSize++; + } + m_nextCode++; + + // if we're out of codes, restart the string table + if (m_nextCode == MAX_LZW_CODE) { + m_partial |= m_clearCode << m_partialSize; + m_partialSize += m_codeSize; + ClearCompressorTable(); + } + + // Only keep the 8 lowest bits (prevent problems with "negative chars") + m_prefix = ch & 0x000FF; + } + + // increment to the next pixel + if (m_bufferShift > 0 + && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) { + m_bufferShift -= m_bpp; + } else { + m_bufferPos++; + m_bufferShift = 8 - m_bpp; + } + + // jump out here if the output buffer is full + if (bufpos - buf == *len) { + return true; + } + + } else { + // Specific behavior for the first pixel of the whole image + + firstPixelPassed = 1; + // Only keep the 8 lowest bits (prevent problems with "negative chars") + m_prefix = ch & 0x000FF; + + // increment to the next pixel + if (m_bufferShift > 0 + && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) { + m_bufferShift -= m_bpp; + } else { + m_bufferPos++; + m_bufferShift = 8 - m_bpp; + } + + // jump out here if the output buffer is full + if (bufpos - buf == *len) { + return true; + } + } + } + + m_bufferSize = 0; + *len = (int)(bufpos - buf); + + return true; +} + +bool StringTable::Decompress(BYTE *buf, int *len) +{ + if (m_bufferSize == 0 || m_done) { + return false; + } + + BYTE *bufpos = buf; + for (; m_bufferPos < m_bufferSize; m_bufferPos++) { + m_partial |= (int)m_buffer[m_bufferPos] << m_partialSize; + m_partialSize += 8; + while (m_partialSize >= m_codeSize) { + int code = m_partial & m_codeMask; + m_partial >>= m_codeSize; + m_partialSize -= m_codeSize; + + if (code > m_nextCode || (m_nextCode == MAX_LZW_CODE && code != m_clearCode) + || code == m_endCode) { + m_done = true; + *len = (int)(bufpos - buf); + return true; + } + if (code == m_clearCode) { + ClearDecompressorTable(); + continue; + } + + // add new string to string table, if not the first pass since a clear code + if (m_oldCode != MAX_LZW_CODE) { + m_strings[m_nextCode] = + m_strings[m_oldCode] + m_strings[code == m_nextCode ? m_oldCode : code][0]; + } + + if ((int)m_strings[code].size() > *len - (bufpos - buf)) { + // out of space, stuff the code back in for next time + m_partial <<= m_codeSize; + m_partialSize += m_codeSize; + m_partial |= code; + m_bufferPos++; + *len = (int)(bufpos - buf); + return true; + } + + // output the string into the buffer + memcpy(bufpos, m_strings[code].data(), m_strings[code].size()); + bufpos += m_strings[code].size(); + + // increment the next highest valid code, add a bit to the mask if we need to increase + // the code size + if (m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE) { + if (++m_nextCode < MAX_LZW_CODE) { + if ((m_nextCode & m_codeMask) == 0) { + m_codeSize++; + m_codeMask |= m_nextCode; + } + } + } + + m_oldCode = code; + } + } + + m_bufferSize = 0; + *len = (int)(bufpos - buf); + + return true; +} + +void StringTable::Done(void) +{ + m_done = true; +} + +void StringTable::ClearCompressorTable(void) +{ + if (m_strmap) { + memset(m_strmap, 0xFF, sizeof(unsigned int) * (1 << 20)); + } + m_nextCode = m_endCode + 1; + + m_prefix = 0; + m_codeSize = m_minCodeSize + 1; +} + +void StringTable::ClearDecompressorTable(void) +{ + for (int i = 0; i < m_clearCode; i++) { + m_strings[i].resize(1); + m_strings[i][0] = (char)i; + } + m_nextCode = m_endCode + 1; + + m_codeSize = m_minCodeSize + 1; + m_codeMask = (1 << m_codeSize) - 1; + m_oldCode = MAX_LZW_CODE; +} + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static BOOL DLL_CALLCONV Validate(FreeImageIO *io, fi_handle handle) +{ + char buf[6]; + if (io->read_proc(buf, 6, 1, handle) < 1) { + return FALSE; + } + + BOOL bResult = FALSE; + if (!strncmp(buf, "GIF", 3)) { + if (buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a' + && buf[5] <= 'z') { + bResult = TRUE; + } + } + + io->seek_proc(handle, -6, SEEK_CUR); + + return bResult; +} + +// ---------------------------------------------------------- + +static void *DLL_CALLCONV Open(FreeImageIO *io, fi_handle handle) +{ + GIFinfo *info = new GIFinfo; + if (info == NULL) { + return NULL; + } + BOOL read = TRUE; + + // 25/02/2008 MDA: Not safe to memset GIFinfo structure with VS 2008 (safe iterators), + // perform initialization in constructor instead. + // memset(info, 0, sizeof(GIFinfo)); + + info->read = read; + try { + // Header + if (!Validate(io, handle)) { + throw "Not a GIF file"; + } + io->seek_proc(handle, 6, SEEK_CUR); + + // Logical Screen Descriptor + io->seek_proc(handle, 4, SEEK_CUR); + BYTE packed; + if (io->read_proc(&packed, 1, 1, handle) < 1) { + throw "EOF reading Logical Screen Descriptor"; + } + if (io->read_proc(&info->background_color, 1, 1, handle) < 1) { + throw "EOF reading Logical Screen Descriptor"; + } + io->seek_proc(handle, 1, SEEK_CUR); + + // Global Color Table + if (packed & GIF_PACKED_LSD_HAVEGCT) { + info->global_color_table_offset = io->tell_proc(handle); + info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE); + io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR); + } + + // Scan through all the rest of the blocks, saving offsets + size_t gce_offset = 0; + BYTE block = 0; + while (block != GIF_BLOCK_TRAILER) { + if (io->read_proc(&block, 1, 1, handle) < 1) { + throw "EOF reading blocks"; + } + if (block == GIF_BLOCK_IMAGE_DESCRIPTOR) { + info->image_descriptor_offsets.push_back(io->tell_proc(handle)); + // GCE may be 0, meaning no GCE preceded this ID + info->graphic_control_extension_offsets.push_back(gce_offset); + gce_offset = 0; + + io->seek_proc(handle, 8, SEEK_CUR); + if (io->read_proc(&packed, 1, 1, handle) < 1) { + throw "EOF reading Image Descriptor"; + } + + // Local Color Table + if (packed & GIF_PACKED_ID_HAVELCT) { + io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR); + } + + // LZW Minimum Code Size + io->seek_proc(handle, 1, SEEK_CUR); + } else if (block == GIF_BLOCK_EXTENSION) { + BYTE ext; + if (io->read_proc(&ext, 1, 1, handle) < 1) { + throw "EOF reading extension"; + } + + if (ext == GIF_EXT_GRAPHIC_CONTROL) { + // overwrite previous offset if more than one GCE found before an ID + gce_offset = io->tell_proc(handle); + } else if (ext == GIF_EXT_COMMENT) { + info->comment_extension_offsets.push_back(io->tell_proc(handle)); + } else if (ext == GIF_EXT_APPLICATION) { + info->application_extension_offsets.push_back(io->tell_proc(handle)); + } + } else if (block == GIF_BLOCK_TRAILER) { + continue; + } else { + throw "Invalid GIF block found"; + } + + // Data Sub-blocks + BYTE len; + if (io->read_proc(&len, 1, 1, handle) < 1) { + throw "EOF reading sub-block"; + } + while (len != 0) { + io->seek_proc(handle, len, SEEK_CUR); + if (io->read_proc(&len, 1, 1, handle) < 1) { + throw "EOF reading sub-block"; + } + } + } + } catch (const char *msg) { + FreeImage_OutputMessageProc(s_format_id, msg, io); + delete info; + return NULL; + } + + return info; +} + +static FIBITMAP *DLL_CALLCONV DoLoadGIF(FreeImageIO *io, fi_handle handle, int flags, void *data) +{ + if (data == NULL) { + return NULL; + } + (void)flags; + GIFinfo *info = (GIFinfo *)data; + + int page = 0; + FIBITMAP *dib = NULL; + try { + bool have_transparent = false, no_local_palette = false, interlaced = false; + int disposal_method = GIF_DISPOSAL_LEAVE, transparent_color = 0; + WORD left, top, width, height; + BYTE packed, b; + WORD w; + + // Image Descriptor + io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET); + io->read_proc(&left, 2, 1, handle); + io->read_proc(&top, 2, 1, handle); + io->read_proc(&width, 2, 1, handle); + io->read_proc(&height, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&left); + SwapShort(&top); + SwapShort(&width); + SwapShort(&height); +#endif + io->read_proc(&packed, 1, 1, handle); + interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false; + no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true; + + int bpp = 8; + if (!no_local_palette) { + int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); + if (size <= 2) + bpp = 1; + else if (size <= 16) + bpp = 4; + } else if (info->global_color_table_offset != 0) { + if (info->global_color_table_size <= 2) + bpp = 1; + else if (info->global_color_table_size <= 16) + bpp = 4; + } + dib = FreeImage_Allocate(width, height, bpp, io); + if (dib == NULL) { + throw "DIB allocated failed"; + } + + // Palette + RGBQUAD *pal = FreeImage_GetPalette(dib); + if (!no_local_palette) { + int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); + + int i = 0; + while (i < size) { + io->read_proc(&pal[i].rgbRed, 1, 1, handle); + io->read_proc(&pal[i].rgbGreen, 1, 1, handle); + io->read_proc(&pal[i].rgbBlue, 1, 1, handle); + i++; + } + } else if (info->global_color_table_offset != 0) { + long pos = io->tell_proc(handle); + io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET); + + int i = 0; + while (i < info->global_color_table_size) { + io->read_proc(&pal[i].rgbRed, 1, 1, handle); + io->read_proc(&pal[i].rgbGreen, 1, 1, handle); + io->read_proc(&pal[i].rgbBlue, 1, 1, handle); + i++; + } + + io->seek_proc(handle, pos, SEEK_SET); + } else { + // its legal to have no palette, but we're going to generate *something* + for (int i = 0; i < 256; i++) { + pal[i].rgbRed = (BYTE)i; + pal[i].rgbGreen = (BYTE)i; + pal[i].rgbBlue = (BYTE)i; + } + } + + // LZW Minimum Code Size + io->read_proc(&b, 1, 1, handle); + StringTable *stringtable = new StringTable; + stringtable->Initialize(b); + + // Image Data Sub-blocks + int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0; + BYTE *scanline = FreeImage_GetScanLine(dib, height - 1); + BYTE buf[4096]; + io->read_proc(&b, 1, 1, handle); + while (b) { + io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle); + int size = sizeof(buf); + while (stringtable->Decompress(buf, &size)) { + for (int i = 0; i < size; i++) { + scanline[xpos] |= (buf[i] & mask) << shift; + if (shift > 0) { + shift -= bpp; + } else { + xpos++; + shift = 8 - bpp; + } + if (++x >= width) { + if (interlaced) { + y += g_GifInterlaceIncrement[interlacepass]; + if (y >= height && ++interlacepass < GIF_INTERLACE_PASSES) { + y = g_GifInterlaceOffset[interlacepass]; + } + } else { + y++; + } + if (y >= height) { + stringtable->Done(); + break; + } + x = xpos = 0; + shift = 8 - bpp; + scanline = FreeImage_GetScanLine(dib, height - y - 1); + } + } + size = sizeof(buf); + } + io->read_proc(&b, 1, 1, handle); + } + + if (page == 0) { + QT3DSU32 idx; + + // Logical Screen Descriptor + io->seek_proc(handle, 6, SEEK_SET); + WORD logicalwidth, logicalheight; + io->read_proc(&logicalwidth, 2, 1, handle); + io->read_proc(&logicalheight, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&logicalwidth); + SwapShort(&logicalheight); +#endif + + // Global Color Table + if (info->global_color_table_offset != 0) { + RGBQUAD globalpalette[256]; + io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET); + int i = 0; + while (i < info->global_color_table_size) { + io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle); + io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle); + io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle); + globalpalette[i].rgbReserved = 0; + i++; + } + // background color + if (info->background_color < info->global_color_table_size) { + FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]); + } + } + + // Application Extension + LONG loop = 1; // If no AE with a loop count is found, the default must be 1 + for (idx = 0; idx < info->application_extension_offsets.size(); idx++) { + io->seek_proc(handle, (long)info->application_extension_offsets[idx], SEEK_SET); + io->read_proc(&b, 1, 1, handle); + if (b == 11) { // All AEs start with an 11 byte sub-block to determine what type of + // AE it is + char buf[11]; + io->read_proc(buf, 11, 1, handle); + if (!memcmp(buf, "NETSCAPE2.0", 11) + || !memcmp(buf, "ANIMEXTS1.0", + 11)) { // Not everybody recognizes ANIMEXTS1.0 but it is valid + io->read_proc(&b, 1, 1, handle); + if (b == 3) { // we're supposed to have a 3 byte sub-block now + io->read_proc(&b, 1, 1, + handle); // this should be 0x01 but isn't really important + io->read_proc(&w, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&w); +#endif + loop = w; + if (loop > 0) + loop++; + break; + } + } + } + } + } + + // Graphic Control Extension + if (info->graphic_control_extension_offsets[page] != 0) { + io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), + SEEK_SET); + io->read_proc(&packed, 1, 1, handle); + io->read_proc(&w, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&w); +#endif + io->read_proc(&b, 1, 1, handle); + have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; + disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; + + transparent_color = b; + if (have_transparent) { + int size = 1 << bpp; + if (transparent_color <= size) { + BYTE table[256]; + memset(table, 0xFF, size); + table[transparent_color] = 0; + FreeImage_SetTransparencyTable(dib, table, size); + dib->m_TransparentPaletteIndex = b; + } + } + } + b = (BYTE)disposal_method; + + delete stringtable; + + } catch (const char *msg) { + if (dib != NULL) { + FreeImage_Unload(dib); + } + FreeImage_OutputMessageProc(s_format_id, msg, io); + return NULL; + } + + return dib; +} + +static void DLL_CALLCONV Close(void *data) +{ + if (data == NULL) { + return; + } + GIFinfo *info = (GIFinfo *)data; + delete info; +} + +SLoadedTexture *SLoadedTexture::LoadGIF(ISeekableIOStream &inStream, bool inFlipY, + NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) +{ + Q_UNUSED(renderContextType) + FreeImageIO theIO(inFnd.getAllocator(), inFnd); + void *gifData = Open(&theIO, &inStream); + if (gifData) { + SLoadedTexture *retval = DoLoadGIF(&theIO, &inStream, 0, gifData); + Close(gifData); + if (retval) + retval->FreeImagePostProcess(inFlipY); + return retval; + } + return NULL; +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp new file mode 100644 index 0000000..defff29 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2014 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +// ========================================================== +// Radiance RGBE .HDR Loader +// Decodes Radiance RGBE HDR image into FP16 texture buffer. +// +// Implementation by +// Parashar Krishnamachari (parashark@nvidia.com) +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "Qt3DSRenderLoadedTextureFreeImageCompat.h" +#include "render/Qt3DSRenderBaseTypes.h" + +typedef unsigned char RGBE[4]; +#define R 0 +#define G 1 +#define B 2 +#define E 3 + +#define MINELEN 8 // minimum scanline length for encoding +#define MAXELEN 0x7fff // maximum scanline length for encoding + +static int s_format_id; + +static float convertComponent(int exponent, int val) +{ + float v = val / (256.0f); + float d = powf(2.0f, (float)exponent - 128.0f); + return v * d; +} + +static void decrunchScanlineOld(FreeImageIO *io, fi_handle handle, RGBE *scanline, int width) +{ + int i; + int rshift = 0; + + while (width > 0) { + io->read_proc(scanline, 4, 1, handle); + + // The older version of RLE encodes the length in the exponent. + // and marks a run with 1, 1, 1 in RGB. This is differentiated from + // a raw value of 1, 1, 1, by having a exponent of 0; + if (scanline[0][R] == 1 && scanline[0][G] == 1 && scanline[0][B] == 1) { + for (i = (scanline[0][E] << rshift); i > 0; --i) { + memcpy(&scanline[0][0], &scanline[-1][0], 4); + ++scanline; + --width; + } + rshift += 8; + } else { + ++scanline; + --width; + rshift = 0; + } + } +} + +static void decrunchScanline(FreeImageIO *io, fi_handle handle, RGBE *scanline, int width) +{ + if ((width < MINELEN) || (width > MAXELEN)) { + decrunchScanlineOld(io, handle, scanline, width); + return; + } + + char c; + io->read_proc(&c, 1, 1, handle); + if (c != 2) { + io->seek_proc(handle, -1, SEEK_CUR); + decrunchScanlineOld(io, handle, scanline, width); + return; + } + + io->read_proc(&(scanline[0][G]), 1, 1, handle); + io->read_proc(&(scanline[0][B]), 1, 1, handle); + io->read_proc(&c, 1, 1, handle); + + if (scanline[0][G] != 2 || scanline[0][B] & 128) { + scanline[0][R] = 2; + scanline[0][E] = c; + decrunchScanlineOld(io, handle, scanline + 1, width - 1); + } + + // RLE-encoded version does a separate buffer for each channel per scanline + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < width;) { + unsigned char code, val; + io->read_proc(&code, 1, 1, handle); + if (code + > 128) // RLE-encoded run... read 1 value and copy it forward for some n count. + { + code &= 127; + io->read_proc(&val, 1, 1, handle); + while (code--) + scanline[j++][i] = val; + } else // Not a run, so we read it as raw data + { + // Note -- we store each pixel in memory 4 bytes apart, so we can't just + // do one long read. + while (code--) + io->read_proc(&(scanline[j++][i]), 1, 1, handle); + } + } + } +} + +static void decodeScanlineToTexture(RGBE *scanline, int width, void *outBuf, QT3DSU32 offset, + NVRenderTextureFormats::Enum inFormat) +{ + float rgbaF32[4]; + + for (int i = 0; i < width; ++i) { + rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]); + rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]); + rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]); + rgbaF32[3] = 1.0f; + + QT3DSU8 *target = reinterpret_cast<QT3DSU8 *>(outBuf); + target += offset; + NVRenderTextureFormats::encodeToPixel( + rgbaF32, target, i * NVRenderTextureFormats::getSizeofFormat(inFormat), inFormat); + } +} + +static FIBITMAP *DoLoadHDR(FreeImageIO *io, fi_handle handle, + NVRenderTextureFormats::Enum inFormat = NVRenderTextureFormats::RGB32F) +{ + FIBITMAP *dib = NULL; + try { + if (handle != NULL) { + char str[200]; + int i; + + // Make sure it's a Radiance RGBE file + io->read_proc(str, 10, 1, handle); + if (memcmp(str, "#?RADIANCE", 10)) { + throw "Invalid HDR file"; + } + + io->seek_proc(handle, 1, SEEK_CUR); + + // Get the command string (it's not really important for us; We're always assuming + // 32bit_rle_rgbe is the format + // we're just reading it to skip ahead the correct number of bytes). + i = 0; + char c = 0, prevC; + do { + prevC = c; + io->read_proc(&c, 1, 1, handle); + str[i++] = c; + } while (!(c == 0xa && prevC == 0xa)); + + // Get the resolution string (it will be NULL-terminated for us) + char res[200]; + i = 0; + do { + io->read_proc(&c, 1, 1, handle); + res[i++] = c; + } while (c != 0xa); + res[i] = 0; + + int width, height; + if (!sscanf(res, "-Y %d +X %d", &height, &width)) { + throw "Error encountered while loading HDR stream : could not determine image " + "resolution!"; + } + int bytesPerPixel = NVRenderTextureFormats::getSizeofFormat(inFormat); + dib = FreeImage_Allocate(width, height, bytesPerPixel * 8, io); + if (dib == NULL) { + throw "DIB allocation failed"; + } + + dib->format = inFormat; + dib->components = NVRenderTextureFormats::getNumberOfComponent(inFormat); + + // Allocate a scanline worth of RGBE data + RGBE *scanline = new RGBE[width]; + if (!scanline) { + throw "Error encountered while loading HDR stream : could not buffer scanlines!"; + } + + // Go through all the scanlines + for (int y = 0; y < height; ++y) { + QT3DSU32 byteOfs = (height - 1 - y) * width * bytesPerPixel; + decrunchScanline(io, handle, scanline, width); + decodeScanlineToTexture(scanline, width, dib->data, byteOfs, inFormat); + } + } + return dib; + } catch (const char *message) { + if (dib) { + FreeImage_Unload(dib); + } + if (message) { + FreeImage_OutputMessageProc(s_format_id, message, io); + } + } + + return NULL; +} + +SLoadedTexture *SLoadedTexture::LoadHDR(ISeekableIOStream &inStream, NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) +{ + FreeImageIO theIO(inFnd.getAllocator(), inFnd); + SLoadedTexture *retval = nullptr; + if (renderContextType == qt3ds::render::NVRenderContextValues::GLES2) + retval = DoLoadHDR(&theIO, &inStream, NVRenderTextureFormats::RGBA8); + else + retval = DoLoadHDR(&theIO, &inStream, NVRenderTextureFormats::RGBA16F); + + + // Let's just assume we don't support this just yet. + // if ( retval ) + // retval->FreeImagePostProcess( inFlipY ); + return retval; +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp new file mode 100644 index 0000000..b1d4b05 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://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 "foundation/Qt3DSFoundation.h" +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "Qt3DSRenderLoadedTexture.h" +#include "Qt3DSRenderLoadedTextureKTX.h" +#include "Qt3DSRenderLoadedTextureDDS.h" + +#include <qendian.h> +#include <qopengltexture.h> + +using namespace qt3ds::render; +using namespace qt3ds::foundation; + +namespace qt3ds { +namespace render { + +static inline int blockSizeForTextureFormat(int format) +{ + switch (format) { + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RGB_DXT1: + return 8; + + default: + return 16; + } +} + +static inline int runtimeFormat(quint32 internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::RGB8_ETC1: + return NVRenderTextureFormats::RGB8_ETC1; + case QOpenGLTexture::RGB8_ETC2: + return NVRenderTextureFormats::RGB8_ETC2; + case QOpenGLTexture::SRGB8_ETC2: + return NVRenderTextureFormats::SRGB8_ETC2; + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + return NVRenderTextureFormats::RGB8_PunchThrough_Alpha1_ETC2; + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + return NVRenderTextureFormats::SRGB8_PunchThrough_Alpha1_ETC2; + case QOpenGLTexture::R11_EAC_UNorm: + return NVRenderTextureFormats::R11_EAC_UNorm; + case QOpenGLTexture::R11_EAC_SNorm: + return NVRenderTextureFormats::R11_EAC_SNorm; + case QOpenGLTexture::RGB_DXT1: + return NVRenderTextureFormats::RGB_DXT1; + case QOpenGLTexture::RGBA_DXT1: + return NVRenderTextureFormats::RGBA_DXT3; + case QOpenGLTexture::RGBA_DXT3: + return NVRenderTextureFormats::RGBA_DXT3; + case QOpenGLTexture::RGBA_DXT5: + return NVRenderTextureFormats::RGBA_DXT5; + default: + break; + } + return NVRenderTextureFormats::Unknown; +} + +static inline int imageSize(QT3DSI32 width, QT3DSI32 height, const Qt3DSDDSImage *image) +{ + return ((width + 3) / 4) * ((height + 3) / 4) + * blockSizeForTextureFormat(image->internalFormat); +} + +static inline quint32 totalImageDataSize(Qt3DSDDSImage *image) +{ + int i, j; + int index = 0; + quint32 size = 0; + int w, h; + int cubeCount = image->cubemap ? 6 : 1; + + for (j = 0; j < cubeCount; j++) { + w = image->width; + h = image->height; + + for (i = 0; i < image->numMipmaps; i++) // account for base plus each mip + { + size += 4; // image size is saved in the file + image->size[index] = imageSize(w, h, image); + image->mipwidth[index] = w; + image->mipheight[index] = h; + size += quint32(image->size[index]); + if (w != 1) + w >>= 1; + if (h != 1) + h >>= 1; + + index++; + } + } + + return (size); +} + +static inline quint32 alignedOffset(quint32 offset, quint32 byteAlign) +{ + return (offset + byteAlign - 1) & ~(byteAlign - 1); +} + +inline SLoadedTexture *loadKtx(NVAllocatorCallback &allocator, IInStream &inStream, + QT3DSI32 flipVertical) +{ + static const int KTX_IDENTIFIER_LENGTH = 12; + static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { + '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' + }; + static const quint32 platformEndianIdentifier = 0x04030201; + static const quint32 inversePlatformEndianIdentifier = 0x01020304; + + struct KTXHeader { + quint8 identifier[KTX_IDENTIFIER_LENGTH]; + quint32 endianness; + quint32 glType; + quint32 glTypeSize; + quint32 glFormat; + quint32 glInternalFormat; + quint32 glBaseInternalFormat; + quint32 pixelWidth; + quint32 pixelHeight; + quint32 pixelDepth; + quint32 numberOfArrayElements; + quint32 numberOfFaces; + quint32 numberOfMipmapLevels; + quint32 bytesOfKeyValueData; + }; + + KTXHeader header; + if (inStream.Read(header) != sizeof(header) + || qstrncmp(reinterpret_cast<char *>(header.identifier), + ktxIdentifier, KTX_IDENTIFIER_LENGTH) != 0 + || (header.endianness != platformEndianIdentifier + && header.endianness != inversePlatformEndianIdentifier)) { + return nullptr; + } + + const bool isInverseEndian = (header.endianness == inversePlatformEndianIdentifier); + auto decode = [isInverseEndian](quint32 val) { + return isInverseEndian ? qbswap<quint32>(val) : val; + }; + + const bool isCompressed = decode(header.glType) == 0 && decode(header.glFormat) == 0 + && decode(header.glTypeSize) == 1; + if (!isCompressed) { + qWarning("Uncompressed ktx texture data is not supported"); + return nullptr; + } + + if (decode(header.numberOfArrayElements) != 0) { + qWarning("Array ktx textures not supported"); + return nullptr; + } + + if (decode(header.pixelDepth) != 0) { + qWarning("Only 2D and cube ktx textures are supported"); + return nullptr; + } + + const int bytesToSkip = int(decode(header.bytesOfKeyValueData)); + QVector<uint8_t> skipData; + skipData.resize(bytesToSkip); + if (inStream.Read(NVDataRef<uint8_t>(skipData.data(), bytesToSkip)) != bytesToSkip) { + qWarning("Unexpected end of ktx data"); + return nullptr; + } + + // now for each mipmap level we have (arrays and 3d textures not supported here) + // uint32 imageSize + // for each array element + // for each face + // for each z slice + // compressed data + // padding so that each face data starts at an offset that is a multiple of 4 + // padding so that each imageSize starts at an offset that is a multiple of 4 + + Qt3DSDDSImage *image = (Qt3DSDDSImage *)QT3DS_ALLOC(allocator, sizeof(Qt3DSDDSImage), "DoLoadDDS"); + + const quint32 level0Width = decode(header.pixelWidth); + const quint32 level0Height = decode(header.pixelHeight); + quint32 faceCount = decode(header.numberOfFaces); + const quint32 mipMapLevels = decode(header.numberOfMipmapLevels); + const quint32 format = decode(header.glInternalFormat); + image->numMipmaps = int(mipMapLevels); + image->cubemap = faceCount == 6 ? 6 : 0; + image->internalFormat = int(format); + image->format = runtimeFormat(format); + image->width = int(level0Width); + image->height = int(level0Height); + image->compressed = 1; + quint32 totalSize = totalImageDataSize(image); + image->dataBlock = QT3DS_ALLOC(allocator, totalSize, "Qt3DSDDSAllocDataBlock"); + if (inStream.Read(NVDataRef<uint8_t>(reinterpret_cast<uint8_t*>(image->dataBlock), totalSize)) + != totalSize) { + QT3DS_FREE(allocator, image); + return nullptr; + } + + SLoadedTexture *result = QT3DS_NEW(allocator, SLoadedTexture)(allocator); + result->dds = image; + result->width = int(level0Width); + result->height = int(level0Height); + result->format = static_cast<NVRenderTextureFormats::Enum>(image->format); + + // TODO: Proper support for cubemaps should be implemented at some point. + if (faceCount > 1) { + qWarning("Multiple faces (cubemaps) not currently supported in ktx"); + faceCount = 1; + } + + uint8_t *p = reinterpret_cast<uint8_t *>(image->dataBlock); + uint8_t *basep = p; + + for (quint32 mip = 0; mip < mipMapLevels; ++mip) { + if (p + 4 - basep > totalSize) + break; + const quint32 imageSize = *reinterpret_cast<const quint32 *>(p); + p += 4; + for (quint32 face = 0; face < faceCount; ++face) { + const quint32 nextOffset = quint32(p + imageSize - basep); + if (nextOffset > totalSize) + break; + image->data[mip] = reinterpret_cast<void *>(p); + p = basep + alignedOffset(nextOffset, 4); + } + } + + return result; +} + +SLoadedTexture *SLoadedTexture::LoadKTX(IInStream &inStream, QT3DSI32 flipVertical, + NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) +{ + Q_UNUSED(renderContextType) + SLoadedTexture *retval = loadKtx(inFnd.getAllocator(), inStream, flipVertical); + + return retval; +} + +} +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h new file mode 100644 index 0000000..3a2d762 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://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 QT3DS_RENDER_LOAD_KTX_H +#define QT3DS_RENDER_LOAD_KTX_H + +namespace qt3ds { +namespace render { + +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp new file mode 100644 index 0000000..023964f --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp @@ -0,0 +1,599 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2016 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderPrefilterTexture.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderShaderProgram.h" + +#include <string> + +using namespace qt3ds; +using namespace qt3ds::render; +using namespace qt3ds::foundation; + +Qt3DSRenderPrefilterTexture::Qt3DSRenderPrefilterTexture(NVRenderContext *inNVRenderContext, + QT3DSI32 inWidth, QT3DSI32 inHeight, + NVRenderTexture2D &inTexture2D, + NVRenderTextureFormats::Enum inDestFormat, + NVFoundationBase &inFnd) + : m_Foundation(inFnd) + , mRefCount(0) + , m_Texture2D(inTexture2D) + , m_DestinationFormat(inDestFormat) + , m_Width(inWidth) + , m_Height(inHeight) + , m_NVRenderContext(inNVRenderContext) +{ + // Calculate mip level + int maxDim = inWidth >= inHeight ? inWidth : inHeight; + + m_MaxMipMapLevel = static_cast<int>(logf((float)maxDim) / logf(2.0f)); + // no concept of sizeOfFormat just does'nt make sense + m_SizeOfFormat = NVRenderTextureFormats::getSizeofFormat(m_DestinationFormat); + m_NoOfComponent = NVRenderTextureFormats::getNumberOfComponent(m_DestinationFormat); +} + +Qt3DSRenderPrefilterTexture * +Qt3DSRenderPrefilterTexture::Create(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight, + NVRenderTexture2D &inTexture2D, + NVRenderTextureFormats::Enum inDestFormat, + qt3ds::NVFoundationBase &inFnd) +{ + Qt3DSRenderPrefilterTexture *theBSDFMipMap = NULL; + + if (inNVRenderContext->IsComputeSupported()) { + theBSDFMipMap = QT3DS_NEW(inFnd.getAllocator(), Qt3DSRenderPrefilterTextureCompute)( + inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, inFnd); + } + + if (!theBSDFMipMap) { + theBSDFMipMap = QT3DS_NEW(inFnd.getAllocator(), Qt3DSRenderPrefilterTextureCPU)( + inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, inFnd); + } + + if (theBSDFMipMap) + theBSDFMipMap->addRef(); + + return theBSDFMipMap; +} + +Qt3DSRenderPrefilterTexture::~Qt3DSRenderPrefilterTexture() +{ +} + +//------------------------------------------------------------------------------------ +// CPU based filtering +//------------------------------------------------------------------------------------ + +Qt3DSRenderPrefilterTextureCPU::Qt3DSRenderPrefilterTextureCPU( + NVRenderContext *inNVRenderContext, int inWidth, int inHeight, NVRenderTexture2D &inTexture2D, + NVRenderTextureFormats::Enum inDestFormat, NVFoundationBase &inFnd) + : Qt3DSRenderPrefilterTexture(inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, + inFnd) +{ +} + +inline int Qt3DSRenderPrefilterTextureCPU::wrapMod(int a, int base) +{ + return (a >= 0) ? a % base : (a % base) + base; +} + +inline void Qt3DSRenderPrefilterTextureCPU::getWrappedCoords(int &sX, int &sY, int width, int height) +{ + if (sY < 0) { + sX -= width >> 1; + sY = -sY; + } + if (sY >= height) { + sX += width >> 1; + sY = height - sY; + } + sX = wrapMod(sX, width); +} + +STextureData +Qt3DSRenderPrefilterTextureCPU::CreateBsdfMipLevel(STextureData &inCurMipLevel, + STextureData &inPrevMipLevel, int width, + int height) //, IPerfTimer& inPerfTimer ) +{ + STextureData retval; + int newWidth = width >> 1; + int newHeight = height >> 1; + newWidth = newWidth >= 1 ? newWidth : 1; + newHeight = newHeight >= 1 ? newHeight : 1; + + if (inCurMipLevel.data) { + retval = inCurMipLevel; + retval.dataSizeInBytes = + newWidth * newHeight * NVRenderTextureFormats::getSizeofFormat(inPrevMipLevel.format); + } else { + retval.dataSizeInBytes = + newWidth * newHeight * NVRenderTextureFormats::getSizeofFormat(inPrevMipLevel.format); + retval.format = inPrevMipLevel.format; // inLoadedImage.format; + retval.data = m_Foundation.getAllocator().allocate( + retval.dataSizeInBytes, "Bsdf Scaled Image Data", __FILE__, __LINE__); + } + + for (int y = 0; y < newHeight; ++y) { + for (int x = 0; x < newWidth; ++x) { + float accumVal[4]; + accumVal[0] = 0; + accumVal[1] = 0; + accumVal[2] = 0; + accumVal[3] = 0; + for (int sy = -2; sy <= 2; ++sy) { + for (int sx = -2; sx <= 2; ++sx) { + int sampleX = sx + (x << 1); + int sampleY = sy + (y << 1); + getWrappedCoords(sampleX, sampleY, width, height); + + // Cauchy filter (this is simply because it's the easiest to evaluate, and + // requires no complex + // functions). + float filterPdf = 1.f / (1.f + float(sx * sx + sy * sy) * 2.f); + // With FP HDR formats, we're not worried about intensity loss so much as + // unnecessary energy gain, + // whereas with LDR formats, the fear with a continuous normalization factor is + // that we'd lose + // intensity and saturation as well. + filterPdf /= (NVRenderTextureFormats::getSizeofFormat(retval.format) >= 8) + ? 4.71238898f + : 4.5403446f; + // filterPdf /= 4.5403446f; // Discrete normalization factor + // filterPdf /= 4.71238898f; // Continuous normalization factor + float curPix[4]; + QT3DSI32 byteOffset = (sampleY * width + sampleX) + * NVRenderTextureFormats::getSizeofFormat(retval.format); + if (byteOffset < 0) { + sampleY = height + sampleY; + byteOffset = (sampleY * width + sampleX) + * NVRenderTextureFormats::getSizeofFormat(retval.format); + } + + NVRenderTextureFormats::decodeToFloat(inPrevMipLevel.data, byteOffset, curPix, + retval.format); + + accumVal[0] += filterPdf * curPix[0]; + accumVal[1] += filterPdf * curPix[1]; + accumVal[2] += filterPdf * curPix[2]; + accumVal[3] += filterPdf * curPix[3]; + } + } + + QT3DSU32 newIdx = + (y * newWidth + x) * NVRenderTextureFormats::getSizeofFormat(retval.format); + + NVRenderTextureFormats::encodeToPixel(accumVal, retval.data, newIdx, retval.format); + } + } + + return retval; +} + +void Qt3DSRenderPrefilterTextureCPU::Build(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) +{ + + m_InternalFormat = inFormat; + m_SizeOfInternalFormat = NVRenderTextureFormats::getSizeofFormat(m_InternalFormat); + m_InternalNoOfComponent = NVRenderTextureFormats::getNumberOfComponent(m_InternalFormat); + + m_Texture2D.SetTextureData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0, + m_Width, m_Height, inFormat, m_DestinationFormat); + + STextureData theMipImage; + STextureData prevImage; + prevImage.data = inTextureData; + prevImage.dataSizeInBytes = inTextureDataSize; + prevImage.format = inFormat; + int curWidth = m_Width; + int curHeight = m_Height; + int size = NVRenderTextureFormats::getSizeofFormat(m_InternalFormat); + for (int idx = 1; idx <= m_MaxMipMapLevel; ++idx) { + theMipImage = + CreateBsdfMipLevel(theMipImage, prevImage, curWidth, curHeight); //, m_PerfTimer ); + curWidth = curWidth >> 1; + curHeight = curHeight >> 1; + curWidth = curWidth >= 1 ? curWidth : 1; + curHeight = curHeight >= 1 ? curHeight : 1; + inTextureDataSize = curWidth * curHeight * size; + + m_Texture2D.SetTextureData(toU8DataRef((char *)theMipImage.data, (QT3DSU32)inTextureDataSize), + (QT3DSU8)idx, (QT3DSU32)curWidth, (QT3DSU32)curHeight, theMipImage.format, + m_DestinationFormat); + + if (prevImage.data == inTextureData) + prevImage = STextureData(); + + STextureData temp = prevImage; + prevImage = theMipImage; + theMipImage = temp; + } + QT3DS_FREE(m_Foundation.getAllocator(), theMipImage.data); + QT3DS_FREE(m_Foundation.getAllocator(), prevImage.data); +} + +//------------------------------------------------------------------------------------ +// GL compute based filtering +//------------------------------------------------------------------------------------ + +static const char *computeUploadShader(std::string &prog, NVRenderTextureFormats::Enum inFormat, + bool binESContext) +{ + if (binESContext) { + prog += "#version 310 es\n" + "#extension GL_ARB_compute_shader : enable\n" + "precision highp float;\n" + "precision highp int;\n" + "precision mediump image2D;\n"; + } else { + prog += "#version 430\n" + "#extension GL_ARB_compute_shader : enable\n"; + } + + if (inFormat == NVRenderTextureFormats::RGBA8) { + prog += "// Set workgroup layout;\n" + "layout (local_size_x = 16, local_size_y = 16) in;\n\n" + "layout (rgba8, binding = 1) readonly uniform image2D inputImage;\n\n" + "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n" + "void main()\n" + "{\n" + " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y " + ">= gl_NumWorkGroups.y )\n" + " return;\n" + " vec4 value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy));\n" + " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), value );\n" + "}\n"; + } else { + prog += "float convertToFloat( in uint inValue )\n" + "{\n" + " uint v = inValue & uint(0xFF);\n" + " float f = float(v)/256.0;\n" + " return f;\n" + "}\n"; + + prog += "int getMod( in int inValue, in int mod )\n" + "{\n" + " int v = mod * (inValue/mod);\n" + " return inValue - v;\n" + "}\n"; + + prog += "vec4 getRGBValue( in int byteNo, vec4 inVal, vec4 inVal1 )\n" + "{\n" + " vec4 result= vec4(0.0);\n" + " if( byteNo == 0) {\n" + " result.r = inVal.r;\n" + " result.g = inVal.g;\n" + " result.b = inVal.b;\n" + " }\n" + " else if( byteNo == 1) {\n" + " result.r = inVal.g;\n" + " result.g = inVal.b;\n" + " result.b = inVal.a;\n" + " }\n" + " else if( byteNo == 2) {\n" + " result.r = inVal.b;\n" + " result.g = inVal.a;\n" + " result.b = inVal1.r;\n" + " }\n" + " else if( byteNo == 3) {\n" + " result.r = inVal.a;\n" + " result.g = inVal1.r;\n" + " result.b = inVal1.g;\n" + " }\n" + " return result;\n" + "}\n"; + + prog += "// Set workgroup layout;\n" + "layout (local_size_x = 16, local_size_y = 16) in;\n\n" + "layout (rgba8, binding = 1) readonly uniform image2D inputImage;\n\n" + "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n" + "void main()\n" + "{\n" + " vec4 result = vec4(0.0);\n" + " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y " + ">= gl_NumWorkGroups.y )\n" + " return;\n" + " int xpos = (int(gl_GlobalInvocationID.x)*3)/4;\n" + " int xmod = getMod(int(gl_GlobalInvocationID.x)*3, 4);\n" + " ivec2 readPos = ivec2(xpos, gl_GlobalInvocationID.y);\n" + " vec4 value = imageLoad(inputImage, readPos);\n" + " vec4 value1 = imageLoad(inputImage, ivec2(readPos.x + 1, readPos.y));\n" + " result = getRGBValue( xmod, value, value1);\n" + " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), result );\n" + "}\n"; + } + return prog.c_str(); +} + +static const char *computeWorkShader(std::string &prog, bool binESContext) +{ + if (binESContext) { + prog += "#version 310 es\n" + "#extension GL_ARB_compute_shader : enable\n" + "precision highp float;\n" + "precision highp int;\n" + "precision mediump image2D;\n"; + } else { + prog += "#version 430\n" + "#extension GL_ARB_compute_shader : enable\n"; + } + + prog += "int wrapMod( in int a, in int base )\n" + "{\n" + " return ( a >= 0 ) ? a % base : -(a % base) + base;\n" + "}\n"; + + prog += "void getWrappedCoords( inout int sX, inout int sY, in int width, in int height )\n" + "{\n" + " if (sY < 0) { sX -= width >> 1; sY = -sY; }\n" + " if (sY >= height) { sX += width >> 1; sY = height - sY; }\n" + " sX = wrapMod( sX, width );\n" + "}\n"; + + prog += "// Set workgroup layout;\n" + "layout (local_size_x = 16, local_size_y = 16) in;\n\n" + "layout (rgba16f, binding = 1) readonly uniform image2D inputImage;\n\n" + "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n" + "void main()\n" + "{\n" + " int prevWidth = int(gl_NumWorkGroups.x) << 1;\n" + " int prevHeight = int(gl_NumWorkGroups.y) << 1;\n" + " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y >= " + "gl_NumWorkGroups.y )\n" + " return;\n" + " vec4 accumVal = vec4(0.0);\n" + " for ( int sy = -2; sy <= 2; ++sy )\n" + " {\n" + " for ( int sx = -2; sx <= 2; ++sx )\n" + " {\n" + " int sampleX = sx + (int(gl_GlobalInvocationID.x) << 1);\n" + " int sampleY = sy + (int(gl_GlobalInvocationID.y) << 1);\n" + " getWrappedCoords(sampleX, sampleY, prevWidth, prevHeight);\n" + " if ((sampleY * prevWidth + sampleX) < 0 )\n" + " sampleY = prevHeight + sampleY;\n" + " ivec2 pos = ivec2(sampleX, sampleY);\n" + " vec4 value = imageLoad(inputImage, pos);\n" + " float filterPdf = 1.0 / ( 1.0 + float(sx*sx + sy*sy)*2.0 );\n" + " filterPdf /= 4.71238898;\n" + " accumVal[0] += filterPdf * value.r;\n" + " accumVal[1] += filterPdf * value.g;\n" + " accumVal[2] += filterPdf * value.b;\n" + " accumVal[3] += filterPdf * value.a;\n" + " }\n" + " }\n" + " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), accumVal );\n" + "}\n"; + + return prog.c_str(); +} + +inline NVConstDataRef<QT3DSI8> toRef(const char *data) +{ + size_t len = strlen(data) + 1; + return NVConstDataRef<QT3DSI8>((const QT3DSI8 *)data, (QT3DSU32)len); +} + +static bool isGLESContext(NVRenderContext *context) +{ + NVRenderContextType ctxType = context->GetRenderContextType(); + + // Need minimum of GL3 or GLES3 + if (ctxType == NVRenderContextValues::GLES2 || ctxType == NVRenderContextValues::GLES3 + || ctxType == NVRenderContextValues::GLES3PLUS) { + return true; + } + + return false; +} + +#define WORKGROUP_SIZE 16 + +Qt3DSRenderPrefilterTextureCompute::Qt3DSRenderPrefilterTextureCompute( + NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight, + NVRenderTexture2D &inTexture2D, NVRenderTextureFormats::Enum inDestFormat, + NVFoundationBase &inFnd) + : Qt3DSRenderPrefilterTexture(inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, + inFnd) + , m_BSDFProgram(NULL) + , m_UploadProgram_RGBA8(NULL) + , m_UploadProgram_RGB8(NULL) + , m_Level0Tex(NULL) + , m_TextureCreated(false) +{ +} + +Qt3DSRenderPrefilterTextureCompute::~Qt3DSRenderPrefilterTextureCompute() +{ + m_UploadProgram_RGB8 = NULL; + m_UploadProgram_RGBA8 = NULL; + m_BSDFProgram = NULL; + m_Level0Tex = NULL; +} + +void Qt3DSRenderPrefilterTextureCompute::createComputeProgram(NVRenderContext *context) +{ + std::string computeProg; + + if (!m_BSDFProgram) { + m_BSDFProgram = context + ->CompileComputeSource( + "Compute BSDF mipmap shader", + toRef(computeWorkShader(computeProg, isGLESContext(context)))) + .mShader; + } +} + +NVRenderShaderProgram *Qt3DSRenderPrefilterTextureCompute::getOrCreateUploadComputeProgram( + NVRenderContext *context, NVRenderTextureFormats::Enum inFormat) +{ + std::string computeProg; + + if (inFormat == NVRenderTextureFormats::RGB8) { + if (!m_UploadProgram_RGB8) { + m_UploadProgram_RGB8 = + context + ->CompileComputeSource( + "Compute BSDF mipmap level 0 RGB8 shader", + toRef(computeUploadShader(computeProg, inFormat, isGLESContext(context)))) + .mShader; + } + + return m_UploadProgram_RGB8; + } else { + if (!m_UploadProgram_RGBA8) { + m_UploadProgram_RGBA8 = + context + ->CompileComputeSource( + "Compute BSDF mipmap level 0 RGBA8 shader", + toRef(computeUploadShader(computeProg, inFormat, isGLESContext(context)))) + .mShader; + } + + return m_UploadProgram_RGBA8; + } +} + +void Qt3DSRenderPrefilterTextureCompute::CreateLevel0Tex(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) +{ + NVRenderTextureFormats::Enum theFormat = inFormat; + QT3DSI32 theWidth = m_Width; + + // Since we cannot use RGB format in GL compute + // we treat it as a RGBA component format + if (inFormat == NVRenderTextureFormats::RGB8) { + // This works only with 4 byte aligned data + QT3DS_ASSERT(m_Width % 4 == 0); + theFormat = NVRenderTextureFormats::RGBA8; + theWidth = (m_Width * 3) / 4; + } + + if (m_Level0Tex == NULL) { + m_Level0Tex = m_NVRenderContext->CreateTexture2D(); + m_Level0Tex->SetTextureStorage(1, theWidth, m_Height, theFormat, theFormat, + NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize)); + } else { + m_Level0Tex->SetTextureSubData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0, + 0, 0, theWidth, m_Height, theFormat); + } +} + +void Qt3DSRenderPrefilterTextureCompute::Build(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) +{ + bool needMipUpload = (inFormat != m_DestinationFormat); + // re-upload data + if (!m_TextureCreated) { + m_Texture2D.SetTextureStorage( + m_MaxMipMapLevel + 1, m_Width, m_Height, m_DestinationFormat, inFormat, (needMipUpload) + ? NVDataRef<QT3DSU8>() + : NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize)); + m_Texture2D.addRef(); + // create a compute shader (if not aloread done) which computes the BSDF mipmaps for this + // texture + createComputeProgram(m_NVRenderContext); + + if (!m_BSDFProgram) { + QT3DS_ASSERT(false); + return; + } + + m_TextureCreated = true; + } else if (!needMipUpload) { + m_Texture2D.SetTextureSubData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0, + 0, 0, m_Width, m_Height, inFormat); + } + + if (needMipUpload) { + CreateLevel0Tex(inTextureData, inTextureDataSize, inFormat); + } + + NVScopedRefCounted<NVRenderImage2D> theInputImage; + NVScopedRefCounted<NVRenderImage2D> theOutputImage; + theInputImage = + m_NVRenderContext->CreateImage2D(&m_Texture2D, NVRenderImageAccessType::ReadWrite); + theOutputImage = + m_NVRenderContext->CreateImage2D(&m_Texture2D, NVRenderImageAccessType::ReadWrite); + + if (needMipUpload && m_Level0Tex) { + NVRenderShaderProgram *uploadProg = + getOrCreateUploadComputeProgram(m_NVRenderContext, inFormat); + if (!uploadProg) + return; + + m_NVRenderContext->SetActiveShader(uploadProg); + + NVScopedRefCounted<NVRenderImage2D> theInputImage0; + theInputImage0 = + m_NVRenderContext->CreateImage2D(m_Level0Tex, NVRenderImageAccessType::ReadWrite); + + theInputImage0->SetTextureLevel(0); + NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedinputImage0("inputImage", + *uploadProg); + theCachedinputImage0.Set(theInputImage0); + + theOutputImage->SetTextureLevel(0); + NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedOutputImage("outputImage", + *uploadProg); + theCachedOutputImage.Set(theOutputImage); + + m_NVRenderContext->DispatchCompute(uploadProg, m_Width, m_Height, 1); + + // sync + NVRenderBufferBarrierFlags flags(NVRenderBufferBarrierValues::ShaderImageAccess); + m_NVRenderContext->SetMemoryBarrier(flags); + } + + int width = m_Width >> 1; + int height = m_Height >> 1; + + m_NVRenderContext->SetActiveShader(m_BSDFProgram); + + for (int i = 1; i <= m_MaxMipMapLevel; ++i) { + theOutputImage->SetTextureLevel(i); + NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedOutputImage("outputImage", + *m_BSDFProgram); + theCachedOutputImage.Set(theOutputImage); + theInputImage->SetTextureLevel(i - 1); + NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedinputImage("inputImage", + *m_BSDFProgram); + theCachedinputImage.Set(theInputImage); + + m_NVRenderContext->DispatchCompute(m_BSDFProgram, width, height, 1); + + width = width > 2 ? width >> 1 : 1; + height = height > 2 ? height >> 1 : 1; + + // sync + NVRenderBufferBarrierFlags flags(NVRenderBufferBarrierValues::ShaderImageAccess); + m_NVRenderContext->SetMemoryBarrier(flags); + } +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h new file mode 100644 index 0000000..e633eb1 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2016 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_PREFILTER_TEXTURE_H +#define QT3DS_RENDER_PREFILTER_TEXTURE_H +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "Qt3DSRender.h" + +#include "Qt3DSTypes.h" +#include "Qt3DSRenderLoadedTexture.h" + +namespace qt3ds { +namespace render { + + class Qt3DSRenderPrefilterTexture : public NVRefCounted + { + public: + Qt3DSRenderPrefilterTexture(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight, + NVRenderTexture2D &inTexture, + NVRenderTextureFormats::Enum inDestFormat, + qt3ds::NVFoundationBase &inFnd); + virtual ~Qt3DSRenderPrefilterTexture(); + + virtual void Build(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) = 0; + + static Qt3DSRenderPrefilterTexture *Create(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, + QT3DSI32 inHeight, NVRenderTexture2D &inTexture, + NVRenderTextureFormats::Enum inDestFormat, + qt3ds::NVFoundationBase &inFnd); + + protected: + NVFoundationBase &m_Foundation; ///< Foundation class for allocations and other base things + volatile QT3DSI32 mRefCount; ///< reference count + + NVRenderTexture2D &m_Texture2D; + NVRenderTextureFormats::Enum m_InternalFormat; + NVRenderTextureFormats::Enum m_DestinationFormat; + + QT3DSI32 m_Width; + QT3DSI32 m_Height; + QT3DSI32 m_MaxMipMapLevel; + QT3DSI32 m_SizeOfFormat; + QT3DSI32 m_SizeOfInternalFormat; + QT3DSI32 m_InternalNoOfComponent; + QT3DSI32 m_NoOfComponent; + NVRenderContext *m_NVRenderContext; + }; + + class Qt3DSRenderPrefilterTextureCPU : public Qt3DSRenderPrefilterTexture + { + public: + Qt3DSRenderPrefilterTextureCPU(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, + QT3DSI32 inHeight, NVRenderTexture2D &inTexture, + NVRenderTextureFormats::Enum inDestFormat, + qt3ds::NVFoundationBase &inFnd); + + void Build(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) override; + + STextureData CreateBsdfMipLevel(STextureData &inCurMipLevel, STextureData &inPrevMipLevel, + QT3DSI32 width, QT3DSI32 height); + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation) + + int wrapMod(int a, int base); + void getWrappedCoords(int &sX, int &sY, int width, int height); + }; + + class Qt3DSRenderPrefilterTextureCompute : public Qt3DSRenderPrefilterTexture + { + public: + Qt3DSRenderPrefilterTextureCompute(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, + QT3DSI32 inHeight, NVRenderTexture2D &inTexture, + NVRenderTextureFormats::Enum inDestFormat, + qt3ds::NVFoundationBase &inFnd); + ~Qt3DSRenderPrefilterTextureCompute(); + + void Build(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat) override; + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation) + + private: + void CreateLevel0Tex(void *inTextureData, QT3DSI32 inTextureDataSize, + NVRenderTextureFormats::Enum inFormat); + + NVScopedRefCounted<NVRenderShaderProgram> m_BSDFProgram; + NVScopedRefCounted<NVRenderShaderProgram> m_UploadProgram_RGBA8; + NVScopedRefCounted<NVRenderShaderProgram> m_UploadProgram_RGB8; + NVScopedRefCounted<NVRenderTexture2D> m_Level0Tex; + bool m_TextureCreated; + + void createComputeProgram(NVRenderContext *context); + NVRenderShaderProgram * + getOrCreateUploadComputeProgram(NVRenderContext *context, + NVRenderTextureFormats::Enum inFormat); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp new file mode 100644 index 0000000..25fbb41 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderResourceBufferObjects.h" + +using namespace qt3ds::render; + +/* + IResourceManager& m_ResourceManager; + NVRenderFrameBuffer* m_FrameBuffer; + */ + +CResourceFrameBuffer::CResourceFrameBuffer(IResourceManager &mgr) + : m_ResourceManager(mgr) + , m_FrameBuffer(NULL) +{ +} + +CResourceFrameBuffer::~CResourceFrameBuffer() +{ + ReleaseFrameBuffer(); +} + +bool CResourceFrameBuffer::EnsureFrameBuffer() +{ + if (!m_FrameBuffer) { + m_FrameBuffer = m_ResourceManager.AllocateFrameBuffer(); + return true; + } + return false; +} + +void CResourceFrameBuffer::ReleaseFrameBuffer() +{ + if (m_FrameBuffer) { + m_ResourceManager.Release(*m_FrameBuffer); + } +} + +CResourceRenderBuffer::CResourceRenderBuffer(IResourceManager &mgr) + : m_ResourceManager(mgr) + , m_RenderBuffer(NULL) +{ +} + +CResourceRenderBuffer::~CResourceRenderBuffer() +{ + ReleaseRenderBuffer(); +} + +bool CResourceRenderBuffer::EnsureRenderBuffer(QT3DSU32 width, QT3DSU32 height, + NVRenderRenderBufferFormats::Enum storageFormat) +{ + if (m_RenderBuffer == NULL || m_Dimensions.m_Width != width || m_Dimensions.m_Height != height + || m_StorageFormat != storageFormat) { + if (m_RenderBuffer == NULL || m_StorageFormat != storageFormat) { + ReleaseRenderBuffer(); + m_RenderBuffer = m_ResourceManager.AllocateRenderBuffer(width, height, storageFormat); + } else + m_RenderBuffer->SetDimensions( + qt3ds::render::NVRenderRenderBufferDimensions(width, height)); + m_Dimensions = m_RenderBuffer->GetDimensions(); + m_StorageFormat = m_RenderBuffer->GetStorageFormat(); + return true; + } + return false; +} + +void CResourceRenderBuffer::ReleaseRenderBuffer() +{ + if (m_RenderBuffer) { + m_ResourceManager.Release(*m_RenderBuffer); + m_RenderBuffer = NULL; + } +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h new file mode 100644 index 0000000..fb54c4d --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_RESOURCE_BUFFER_OBJECTS_H +#define QT3DS_RENDER_RESOURCE_BUFFER_OBJECTS_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" + +namespace qt3ds { +namespace render { + class CResourceFrameBuffer + { + protected: + IResourceManager &m_ResourceManager; + NVRenderFrameBuffer *m_FrameBuffer; + + public: + CResourceFrameBuffer(IResourceManager &mgr); + ~CResourceFrameBuffer(); + bool EnsureFrameBuffer(); + void ReleaseFrameBuffer(); + + IResourceManager &GetResourceManager() { return m_ResourceManager; } + operator NVRenderFrameBuffer *() { return m_FrameBuffer; } + NVRenderFrameBuffer *operator->() + { + QT3DS_ASSERT(m_FrameBuffer); + return m_FrameBuffer; + } + NVRenderFrameBuffer &operator*() + { + QT3DS_ASSERT(m_FrameBuffer); + return *m_FrameBuffer; + } + }; + + class CResourceRenderBuffer + { + protected: + IResourceManager &m_ResourceManager; + NVRenderRenderBuffer *m_RenderBuffer; + qt3ds::render::NVRenderRenderBufferFormats::Enum m_StorageFormat; + qt3ds::render::NVRenderRenderBufferDimensions m_Dimensions; + + public: + CResourceRenderBuffer(IResourceManager &mgr); + ~CResourceRenderBuffer(); + bool EnsureRenderBuffer(QT3DSU32 width, QT3DSU32 height, + NVRenderRenderBufferFormats::Enum storageFormat); + void ReleaseRenderBuffer(); + + operator NVRenderRenderBuffer *() { return m_RenderBuffer; } + NVRenderRenderBuffer *operator->() + { + QT3DS_ASSERT(m_RenderBuffer); + return m_RenderBuffer; + } + NVRenderRenderBuffer &operator*() + { + QT3DS_ASSERT(m_RenderBuffer); + return *m_RenderBuffer; + } + }; +} +} +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp new file mode 100644 index 0000000..3593688 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp @@ -0,0 +1,436 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "render/Qt3DSRenderTexture2DArray.h" +#include "render/Qt3DSRenderTextureCube.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSContainers.h" + +using namespace qt3ds::render; + +namespace { + +struct SResourceManager : public IResourceManager +{ + NVScopedRefCounted<NVRenderContext> m_RenderContext; + // Complete list of all allocated objects + nvvector<NVScopedRefCounted<NVRefCounted>> m_AllocatedObjects; + + nvvector<NVRenderFrameBuffer *> m_FreeFrameBuffers; + nvvector<NVRenderRenderBuffer *> m_FreeRenderBuffers; + nvvector<NVRenderTexture2D *> m_FreeTextures; + nvvector<NVRenderTexture2DArray *> m_FreeTexArrays; + nvvector<NVRenderTextureCube *> m_FreeTexCubes; + nvvector<NVRenderImage2D *> m_FreeImages; + + volatile QT3DSI32 mRefCount; + + SResourceManager(NVRenderContext &ctx) + : m_RenderContext(ctx) + , m_AllocatedObjects(ctx.GetAllocator(), "SResourceManager::m_FrameBuffers") + , m_FreeFrameBuffers(ctx.GetAllocator(), "SResourceManager::m_FreeFrameBuffers") + , m_FreeRenderBuffers(ctx.GetAllocator(), "SResourceManager::m_FreeRenderBuffers") + , m_FreeTextures(ctx.GetAllocator(), "SResourceManager::m_FreeTextures") + , m_FreeTexArrays(ctx.GetAllocator(), "SResourceManager::m_FreeTexArrays") + , m_FreeTexCubes(ctx.GetAllocator(), "SResourceManager::m_FreeTexCubes") + , m_FreeImages(ctx.GetAllocator(), "SResourceManager::m_FreeImages") + , mRefCount(0) + { + } + virtual ~SResourceManager() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext->GetAllocator()) + + NVRenderFrameBuffer *AllocateFrameBuffer() override + { + if (m_FreeFrameBuffers.empty() == true) { + NVRenderFrameBuffer *newBuffer = m_RenderContext->CreateFrameBuffer(); + m_AllocatedObjects.push_back(newBuffer); + m_FreeFrameBuffers.push_back(newBuffer); + } + NVRenderFrameBuffer *retval = m_FreeFrameBuffers.back(); + m_FreeFrameBuffers.pop_back(); + return retval; + } + void Release(NVRenderFrameBuffer &inBuffer) override + { + if (inBuffer.HasAnyAttachment()) { + // Ensure the framebuffer has no attachments. + inBuffer.Attach(NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color1, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color2, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color3, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color4, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color5, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color6, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Color7, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Depth, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + inBuffer.Attach(NVRenderFrameBufferAttachments::Stencil, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + if (m_RenderContext->IsDepthStencilSupported()) + inBuffer.Attach(NVRenderFrameBufferAttachments::DepthStencil, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + } +#ifdef _DEBUG + nvvector<NVRenderFrameBuffer *>::iterator theFind = + eastl::find(m_FreeFrameBuffers.begin(), m_FreeFrameBuffers.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeFrameBuffers.end()); +#endif + m_FreeFrameBuffers.push_back(&inBuffer); + } + + virtual NVRenderRenderBuffer * + AllocateRenderBuffer(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderRenderBufferFormats::Enum inBufferFormat) override + { + // Look for one of this specific size and format. + QT3DSU32 existingMatchIdx = m_FreeRenderBuffers.size(); + for (QT3DSU32 idx = 0, end = existingMatchIdx; idx < end; ++idx) { + NVRenderRenderBuffer *theBuffer = m_FreeRenderBuffers[idx]; + qt3ds::render::NVRenderRenderBufferDimensions theDims = theBuffer->GetDimensions(); + NVRenderRenderBufferFormats::Enum theFormat = theBuffer->GetStorageFormat(); + if (theDims.m_Width == inWidth && theDims.m_Height == inHeight + && theFormat == inBufferFormat) { + // Replace idx with last for efficient erasure (that reorders the vector). + m_FreeRenderBuffers.replace_with_last(idx); + return theBuffer; + } else if (theFormat == inBufferFormat) + existingMatchIdx = idx; + } + // If a specific exact match couldn't be found, just use the buffer with + // the same format and resize it. + if (existingMatchIdx < m_FreeRenderBuffers.size()) { + NVRenderRenderBuffer *theBuffer = m_FreeRenderBuffers[existingMatchIdx]; + m_FreeRenderBuffers.replace_with_last(existingMatchIdx); + theBuffer->SetDimensions(qt3ds::render::NVRenderRenderBufferDimensions(inWidth, inHeight)); + return theBuffer; + } + + NVRenderRenderBuffer *theBuffer = + m_RenderContext->CreateRenderBuffer(inBufferFormat, inWidth, inHeight); + m_AllocatedObjects.push_back(theBuffer); + return theBuffer; + } + void Release(NVRenderRenderBuffer &inBuffer) override + { +#ifdef _DEBUG + nvvector<NVRenderRenderBuffer *>::iterator theFind = + eastl::find(m_FreeRenderBuffers.begin(), m_FreeRenderBuffers.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeRenderBuffers.end()); +#endif + m_FreeRenderBuffers.push_back(&inBuffer); + } + NVRenderTexture2D *SetupAllocatedTexture(NVRenderTexture2D &inTexture) + { + inTexture.SetMinFilter(NVRenderTextureMinifyingOp::Linear); + inTexture.SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return &inTexture; + } + NVRenderTexture2D *AllocateTexture2D(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount, bool immutable) override + { + bool inMultisample = + inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported(); + for (QT3DSU32 idx = 0, end = m_FreeTextures.size(); idx < end; ++idx) { + NVRenderTexture2D *theTexture = m_FreeTextures[idx]; + STextureDetails theDetails = theTexture->GetTextureDetails(); + if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight + && inTextureFormat == theDetails.m_Format + && theTexture->GetSampleCount() == inSampleCount) { + m_FreeTextures.replace_with_last(idx); + return SetupAllocatedTexture(*theTexture); + } + } + // else resize an existing texture. This is very expensive + // note that MSAA textures are not resizable ( in GLES ) + /* + if ( !m_FreeTextures.empty() && !inMultisample ) + { + NVRenderTexture2D* theTexture = m_FreeTextures.back(); + m_FreeTextures.pop_back(); + + // note we could re-use a former MSAA texture + // this causes a entiere destroy of the previous texture object + theTexture->SetTextureData( NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inTextureFormat + ); + + return SetupAllocatedTexture( *theTexture ); + }*/ + // else create a new texture. + NVRenderTexture2D *theTexture = m_RenderContext->CreateTexture2D(); + + if (inMultisample) + theTexture->SetTextureDataMultisample(inSampleCount, inWidth, inHeight, + inTextureFormat); + else if (immutable) + theTexture->SetTextureStorage(1, inWidth, inHeight, inTextureFormat); + else + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inTextureFormat); + + m_AllocatedObjects.push_back(theTexture); + return SetupAllocatedTexture(*theTexture); + } + void Release(NVRenderTexture2D &inBuffer) override + { +#ifdef _DEBUG + nvvector<NVRenderTexture2D *>::iterator theFind = + eastl::find(m_FreeTextures.begin(), m_FreeTextures.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeTextures.end()); +#endif + m_FreeTextures.push_back(&inBuffer); + } + + NVRenderTexture2DArray *AllocateTexture2DArray(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inSlices, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount) override + { + bool inMultisample = + inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported(); + for (QT3DSU32 idx = 0, end = m_FreeTexArrays.size(); idx < end; ++idx) { + NVRenderTexture2DArray *theTexture = m_FreeTexArrays[idx]; + STextureDetails theDetails = theTexture->GetTextureDetails(); + if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight + && theDetails.m_Depth == inSlices && inTextureFormat == theDetails.m_Format + && theTexture->GetSampleCount() == inSampleCount) { + m_FreeTexArrays.replace_with_last(idx); + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + } + + // else resize an existing texture. This should be fairly quick at the driver level. + // note that MSAA textures are not resizable ( in GLES ) + if (!m_FreeTexArrays.empty() && !inMultisample) { + NVRenderTexture2DArray *theTexture = m_FreeTexArrays.back(); + m_FreeTexArrays.pop_back(); + + // note we could re-use a former MSAA texture + // this causes a entiere destroy of the previous texture object + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inSlices, + inTextureFormat); + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + + // else create a new texture. + NVRenderTexture2DArray *theTexture = NULL; + + if (!inMultisample) { + theTexture = m_RenderContext->CreateTexture2DArray(); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inSlices, + inTextureFormat); + } else { + // Not supported yet + return NULL; + } + + m_AllocatedObjects.push_back(theTexture); + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + + void Release(NVRenderTexture2DArray &inBuffer) override + { +#ifdef _DEBUG + nvvector<NVRenderTexture2DArray *>::iterator theFind = + eastl::find(m_FreeTexArrays.begin(), m_FreeTexArrays.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeTexArrays.end()); +#endif + m_FreeTexArrays.push_back(&inBuffer); + } + + NVRenderTextureCube *AllocateTextureCube(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount) override + { + bool inMultisample = + inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported(); + for (QT3DSU32 idx = 0, end = m_FreeTexCubes.size(); idx < end; ++idx) { + NVRenderTextureCube *theTexture = m_FreeTexCubes[idx]; + STextureDetails theDetails = theTexture->GetTextureDetails(); + if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight + && inTextureFormat == theDetails.m_Format + && theTexture->GetSampleCount() == inSampleCount) { + m_FreeTexCubes.replace_with_last(idx); + + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + } + + // else resize an existing texture. This should be fairly quick at the driver level. + // note that MSAA textures are not resizable ( in GLES ) + if (!m_FreeTexCubes.empty() && !inMultisample) { + NVRenderTextureCube *theTexture = m_FreeTexCubes.back(); + m_FreeTexCubes.pop_back(); + + // note we could re-use a former MSAA texture + // this causes a entire destroy of the previous texture object + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosX, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegX, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosY, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegY, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosZ, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegZ, + inWidth, inHeight, inTextureFormat); + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + + // else create a new texture. + NVRenderTextureCube *theTexture = NULL; + + if (!inMultisample) { + theTexture = m_RenderContext->CreateTextureCube(); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosX, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegX, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosY, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegY, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosZ, + inWidth, inHeight, inTextureFormat); + theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegZ, + inWidth, inHeight, inTextureFormat); + } else { + // Not supported yet + return NULL; + } + + m_AllocatedObjects.push_back(theTexture); + theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + return theTexture; + } + + void Release(NVRenderTextureCube &inBuffer) override + { +#ifdef _DEBUG + nvvector<NVRenderTextureCube *>::iterator theFind = + eastl::find(m_FreeTexCubes.begin(), m_FreeTexCubes.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeTexCubes.end()); +#endif + m_FreeTexCubes.push_back(&inBuffer); + } + + NVRenderImage2D *AllocateImage2D(NVRenderTexture2D *inTexture, + NVRenderImageAccessType::Enum inAccess) override + { + if (m_FreeImages.empty() == true) { + NVRenderImage2D *newImage = m_RenderContext->CreateImage2D(inTexture, inAccess); + if (newImage) { + m_AllocatedObjects.push_back(newImage); + m_FreeImages.push_back(newImage); + } + } + + NVRenderImage2D *retval = m_FreeImages.back(); + m_FreeImages.pop_back(); + + return retval; + } + + void Release(NVRenderImage2D &inBuffer) override + { +#ifdef _DEBUG + nvvector<NVRenderImage2D *>::iterator theFind = + eastl::find(m_FreeImages.begin(), m_FreeImages.end(), &inBuffer); + QT3DS_ASSERT(theFind == m_FreeImages.end()); +#endif + m_FreeImages.push_back(&inBuffer); + } + + NVRenderContext &GetRenderContext() override { return *m_RenderContext; } + + void RemoveObjectAllocation(NVRefCounted *obj) { + for (QT3DSU32 idx = 0, end = m_AllocatedObjects.size(); idx < end; ++idx) { + if (obj == m_AllocatedObjects[idx]) { + m_AllocatedObjects.replace_with_last(idx); + break; + } + } + } + + void DestroyFreeSizedResources() + { + for (int idx = m_FreeRenderBuffers.size() - 1; idx >= 0; --idx) { + NVRenderRenderBuffer *obj = m_FreeRenderBuffers[idx]; + m_FreeRenderBuffers.replace_with_last(idx); + RemoveObjectAllocation(obj); + } + for (int idx = m_FreeTextures.size() - 1; idx >= 0; --idx) { + NVRenderTexture2D *obj = m_FreeTextures[idx]; + m_FreeTextures.replace_with_last(idx); + RemoveObjectAllocation(obj); + } + for (int idx = m_FreeTexArrays.size() - 1; idx >= 0; --idx) { + NVRenderTexture2DArray *obj = m_FreeTexArrays[idx]; + m_FreeTexArrays.replace_with_last(idx); + RemoveObjectAllocation(obj); + } + for (int idx = m_FreeTexCubes.size() - 1; idx >= 0; --idx) { + NVRenderTextureCube *obj = m_FreeTexCubes[idx]; + m_FreeTexCubes.replace_with_last(idx); + RemoveObjectAllocation(obj); + } + } +}; +} + +IResourceManager &IResourceManager::CreateResourceManager(NVRenderContext &inContext) +{ + return *QT3DS_NEW(inContext.GetAllocator(), SResourceManager)(inContext); +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h new file mode 100644 index 0000000..675d644 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_RESOURCE_MANAGER_H +#define QT3DS_RENDER_RESOURCE_MANAGER_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSRefCounted.h" +#include "render/Qt3DSRenderBaseTypes.h" + +namespace qt3ds { +namespace render { + /** + * Implements simple pooling of render resources + */ + class IResourceManager : public NVRefCounted + { + protected: + virtual ~IResourceManager() {} + + public: + virtual NVRenderFrameBuffer *AllocateFrameBuffer() = 0; + virtual void Release(NVRenderFrameBuffer &inBuffer) = 0; + virtual NVRenderRenderBuffer * + AllocateRenderBuffer(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderRenderBufferFormats::Enum inBufferFormat) = 0; + virtual void Release(NVRenderRenderBuffer &inBuffer) = 0; + virtual NVRenderTexture2D *AllocateTexture2D(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount = 1, + bool immutable = false) = 0; + virtual void Release(NVRenderTexture2D &inBuffer) = 0; + virtual NVRenderTexture2DArray * + AllocateTexture2DArray(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inSlices, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount = 1) = 0; + virtual void Release(NVRenderTexture2DArray &inBuffer) = 0; + virtual NVRenderTextureCube * + AllocateTextureCube(QT3DSU32 inWidth, QT3DSU32 inHeight, + NVRenderTextureFormats::Enum inTextureFormat, + QT3DSU32 inSampleCount = 1) = 0; + virtual void Release(NVRenderTextureCube &inBuffer) = 0; + virtual NVRenderImage2D *AllocateImage2D(NVRenderTexture2D *inTexture, + NVRenderImageAccessType::Enum inAccess) = 0; + virtual void Release(NVRenderImage2D &inBuffer) = 0; + + virtual NVRenderContext &GetRenderContext() = 0; + virtual void DestroyFreeSizedResources() = 0; + + static IResourceManager &CreateResourceManager(NVRenderContext &inContext); + }; +} +} + +#endif diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp new file mode 100644 index 0000000..e877cbc --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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 "Qt3DSRenderResourceTexture2D.h" + +using namespace qt3ds::render; + +CResourceTexture2D::CResourceTexture2D(IResourceManager &mgr, NVRenderTexture2D *inTexture) + : m_ResourceManager(mgr) + , m_Texture(inTexture) +{ + if (inTexture) + m_TextureDetails = inTexture->GetTextureDetails(); +} + +CResourceTexture2D::CResourceTexture2D(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples) + : m_ResourceManager(mgr) + , m_Texture(NULL) +{ + EnsureTexture(width, height, inFormat, inSamples); +} + +CResourceTexture2D::~CResourceTexture2D() +{ + ReleaseTexture(); +} + +// Returns true if the texture was allocated, false if nothing changed (no allocation). +bool CResourceTexture2D::TextureMatches(QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples) +{ + return m_Texture && m_TextureDetails.m_Width == width && m_TextureDetails.m_Height == height + && m_TextureDetails.m_Format == inFormat && m_TextureDetails.m_SampleCount == inSamples; +} + +bool CResourceTexture2D::EnsureTexture(QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples) +{ + if (TextureMatches(width, height, inFormat, inSamples)) + return false; + + if (m_Texture && inSamples > 1) { + // we cannot resize MSAA textures though release first + ReleaseTexture(); + } + + if (!m_Texture) + m_Texture = m_ResourceManager.AllocateTexture2D(width, height, inFormat, inSamples); + else { + // multisampled textures are immuteable + QT3DS_ASSERT(inSamples == 1); + m_Texture->SetTextureData(NVDataRef<QT3DSU8>(), 0, width, height, inFormat); + } + + m_TextureDetails = m_Texture->GetTextureDetails(); + return true; +} + +void CResourceTexture2D::ReleaseTexture() +{ + if (m_Texture) { + m_ResourceManager.Release(*m_Texture); + ForgetTexture(); + } +} + +void CResourceTexture2D::ForgetTexture() +{ + m_Texture = NULL; +} + +void CResourceTexture2D::StealTexture(CResourceTexture2D &inOther) +{ + ReleaseTexture(); + m_Texture = inOther.m_Texture; + m_TextureDetails = inOther.m_TextureDetails; + inOther.m_Texture = NULL; +} + +CResourceTexture2DArray::CResourceTexture2DArray(IResourceManager &mgr) + : m_ResourceManager(mgr) + , m_Texture(NULL) +{ +} + +CResourceTexture2DArray::CResourceTexture2DArray(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height, + QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, + QT3DSU32 inSamples) + : m_ResourceManager(mgr) + , m_Texture(NULL) +{ + EnsureTexture(width, height, slices, inFormat, inSamples); +} + +CResourceTexture2DArray::~CResourceTexture2DArray() +{ + ReleaseTexture(); +} + +bool CResourceTexture2DArray::TextureMatches(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples) +{ + return m_Texture && m_TextureDetails.m_Depth == slices && m_TextureDetails.m_Width == width + && m_TextureDetails.m_Height == height && m_TextureDetails.m_Format == inFormat + && m_TextureDetails.m_SampleCount == inSamples; +} + +bool CResourceTexture2DArray::EnsureTexture(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples) +{ + if (TextureMatches(width, height, slices, inFormat, inSamples)) + return false; + + if (m_Texture && inSamples > 1) { + // we cannot resize MSAA textures though release first + ReleaseTexture(); + } + + if (!m_Texture) + m_Texture = + m_ResourceManager.AllocateTexture2DArray(width, height, slices, inFormat, inSamples); + else { + // multisampled textures are immuteable + QT3DS_ASSERT(inSamples == 1); + m_Texture->SetTextureData(NVDataRef<QT3DSU8>(), 0, width, height, slices, inFormat); + } + + m_TextureDetails = m_Texture->GetTextureDetails(); + return true; +} + +void CResourceTexture2DArray::ReleaseTexture() +{ + if (m_Texture) { + m_ResourceManager.Release(*m_Texture); + m_Texture = NULL; + } +} + +void CResourceTexture2DArray::StealTexture(CResourceTexture2DArray &inOther) +{ + ReleaseTexture(); + m_Texture = inOther.m_Texture; + m_TextureDetails = inOther.m_TextureDetails; + inOther.m_Texture = NULL; +} diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h new file mode 100644 index 0000000..eb54713 --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_RESOURCE_TEXTURE_2D_H +#define QT3DS_RENDER_RESOURCE_TEXTURE_2D_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "render/Qt3DSRenderTexture2DArray.h" +#include "Qt3DSRenderResourceManager.h" + +namespace qt3ds { +namespace render { + class CResourceTexture2D + { + protected: + IResourceManager &m_ResourceManager; + NVRenderTexture2D *m_Texture; + STextureDetails m_TextureDetails; + + public: + CResourceTexture2D(IResourceManager &mgr, NVRenderTexture2D *inTexture = NULL); + // create and allocate the texture right away. + CResourceTexture2D(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1); + ~CResourceTexture2D(); + // Returns true if the texture matches the specs, false if the texture needs to be + // reallocated + bool TextureMatches(QT3DSU32 width, QT3DSU32 height, NVRenderTextureFormats::Enum inFormat, + QT3DSU32 inSamples = 1); + + // Returns true if the texture was allocated, false if nothing changed (no allocation). + // Note this is the exact opposite of TextureMatches. + bool EnsureTexture(QT3DSU32 width, QT3DSU32 height, NVRenderTextureFormats::Enum inFormat, + QT3DSU32 inSamples = 1); + + // Force release the texture. + void ReleaseTexture(); + NVRenderTexture2D &operator*() + { + QT3DS_ASSERT(m_Texture); + return *m_Texture; + } + NVRenderTexture2D *operator->() + { + QT3DS_ASSERT(m_Texture); + return m_Texture; + } + operator NVRenderTexture2D *() { return m_Texture; } + NVRenderTexture2D *GetTexture() { return m_Texture; } + void ForgetTexture(); + // Enforces single ownership rules. + void StealTexture(CResourceTexture2D &inOther); + }; + + class CResourceTexture2DArray + { + protected: + IResourceManager &m_ResourceManager; + qt3ds::render::NVRenderTexture2DArray *m_Texture; + STextureDetails m_TextureDetails; + + public: + CResourceTexture2DArray(IResourceManager &mgr); + // create and allocate the texture right away. + CResourceTexture2DArray(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1); + ~CResourceTexture2DArray(); + // Returns true if the texture matches the specs, false if the texture needs to be + // reallocated + bool TextureMatches(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1); + + // Returns true if the texture was allocated, false if nothing changed (no allocation). + // Note this is the exact opposite of TextureMatches. + bool EnsureTexture(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices, + NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1); + + // Force release the texture. + void ReleaseTexture(); + qt3ds::render::NVRenderTexture2DArray &operator*() + { + QT3DS_ASSERT(m_Texture); + return *m_Texture; + } + qt3ds::render::NVRenderTexture2DArray *operator->() + { + QT3DS_ASSERT(m_Texture); + return m_Texture; + } + operator qt3ds::render::NVRenderTexture2DArray *() { return m_Texture; } + qt3ds::render::NVRenderTexture2DArray *GetTexture() { return m_Texture; } + // Enforces single ownership rules. + void StealTexture(CResourceTexture2DArray &inOther); + }; +} +} + +#endif diff --git a/src/runtimerender/windows/DynamicLibLoader.h b/src/runtimerender/windows/DynamicLibLoader.h new file mode 100644 index 0000000..09b028b --- /dev/null +++ b/src/runtimerender/windows/DynamicLibLoader.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_WINDOWS_DYNAMIC_LIB_LOADER_H +#define QT3DS_WINDOWS_DYNAMIC_LIB_LOADER_H + +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" + +#include <QLibrary> + +namespace qt3ds { +namespace render { + using namespace qt3ds; + // using namespace qt3ds::render; + + class CLoadedDynamicLibrary + { + QLibrary* m_DLLHandle; + CLoadedDynamicLibrary(QLibrary* hdl) + : m_DLLHandle(hdl) + { + } + CLoadedDynamicLibrary(const CLoadedDynamicLibrary &); + CLoadedDynamicLibrary &operator=(const CLoadedDynamicLibrary &); + + public: + ~CLoadedDynamicLibrary() + { + if (m_DLLHandle) { + m_DLLHandle->unload(); + delete m_DLLHandle; + } + m_DLLHandle = 0; + } + void *FindFunction(const char *name) { return (void*)m_DLLHandle->resolve(name); } + static CLoadedDynamicLibrary *Create(const char *inFullDllPath, NVFoundationBase &fnd) + { + QLibrary* hdl = new QLibrary(inFullDllPath); + if (!hdl->load()) { + qCCritical(INVALID_OPERATION, "Failed to load dynamic library %s: %s", + inFullDllPath, qPrintable(hdl->errorString())); + + delete hdl; + return nullptr; + } + return QT3DS_NEW(fnd.getAllocator(), CLoadedDynamicLibrary)(hdl); + } + }; +} +} + +#endif |