summaryrefslogtreecommitdiffstats
path: root/src/runtimerender
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender')
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp518
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp75
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h75
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h101
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldRenderer.cpp1067
-rw-r--r--src/runtimerender/Qt3DSDistanceFieldRenderer.h171
-rw-r--r--src/runtimerender/Qt3DSFontDatabase.cpp106
-rw-r--r--src/runtimerender/Qt3DSFontDatabase_p.h82
-rw-r--r--src/runtimerender/Qt3DSOffscreenRenderKey.h153
-rw-r--r--src/runtimerender/Qt3DSOffscreenRenderManager.cpp498
-rw-r--r--src/runtimerender/Qt3DSOffscreenRenderManager.h256
-rw-r--r--src/runtimerender/Qt3DSOldNBustedRenderPlugin.cpp118
-rw-r--r--src/runtimerender/Qt3DSOldNBustedRenderPlugin.h102
-rw-r--r--src/runtimerender/Qt3DSOnscreenTextRenderer.cpp421
-rw-r--r--src/runtimerender/Qt3DSQtTextRenderer.cpp655
-rw-r--r--src/runtimerender/Qt3DSRender.h257
-rw-r--r--src/runtimerender/Qt3DSRenderClippingFrustum.cpp85
-rw-r--r--src/runtimerender/Qt3DSRenderClippingFrustum.h161
-rw-r--r--src/runtimerender/Qt3DSRenderContextCore.cpp858
-rw-r--r--src/runtimerender/Qt3DSRenderContextCore.h223
-rw-r--r--src/runtimerender/Qt3DSRenderCustomMaterialRenderContext.h94
-rw-r--r--src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp1265
-rw-r--r--src/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.h69
-rw-r--r--src/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp2131
-rw-r--r--src/runtimerender/Qt3DSRenderCustomMaterialSystem.h174
-rw-r--r--src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp1931
-rw-r--r--src/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.h123
-rw-r--r--src/runtimerender/Qt3DSRenderDynamicObjectSystem.cpp1532
-rw-r--r--src/runtimerender/Qt3DSRenderDynamicObjectSystem.h286
-rw-r--r--src/runtimerender/Qt3DSRenderDynamicObjectSystemCommands.h622
-rw-r--r--src/runtimerender/Qt3DSRenderDynamicObjectSystemUtil.h126
-rw-r--r--src/runtimerender/Qt3DSRenderEffectSystem.cpp1872
-rw-r--r--src/runtimerender/Qt3DSRenderEffectSystem.h209
-rw-r--r--src/runtimerender/Qt3DSRenderEulerAngles.cpp383
-rw-r--r--src/runtimerender/Qt3DSRenderEulerAngles.h142
-rw-r--r--src/runtimerender/Qt3DSRenderGpuProfiler.cpp284
-rw-r--r--src/runtimerender/Qt3DSRenderGraphObjectPickQuery.h124
-rw-r--r--src/runtimerender/Qt3DSRenderGraphObjectSerializer.cpp670
-rw-r--r--src/runtimerender/Qt3DSRenderGraphObjectSerializer.h69
-rw-r--r--src/runtimerender/Qt3DSRenderGraphObjectTypes.h166
-rw-r--r--src/runtimerender/Qt3DSRenderImageScaler.cpp883
-rw-r--r--src/runtimerender/Qt3DSRenderImageScaler.h121
-rw-r--r--src/runtimerender/Qt3DSRenderImageTextureData.h138
-rw-r--r--src/runtimerender/Qt3DSRenderInputStreamFactory.cpp214
-rw-r--r--src/runtimerender/Qt3DSRenderInputStreamFactory.h69
-rw-r--r--src/runtimerender/Qt3DSRenderLightConstantProperties.h198
-rw-r--r--src/runtimerender/Qt3DSRenderMaterialHelpers.h94
-rw-r--r--src/runtimerender/Qt3DSRenderMaterialShaderGenerator.h152
-rw-r--r--src/runtimerender/Qt3DSRenderMesh.h187
-rw-r--r--src/runtimerender/Qt3DSRenderPathManager.cpp1964
-rw-r--r--src/runtimerender/Qt3DSRenderPathManager.h121
-rw-r--r--src/runtimerender/Qt3DSRenderPathMath.h713
-rw-r--r--src/runtimerender/Qt3DSRenderPathRenderContext.h94
-rw-r--r--src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.cpp311
-rw-r--r--src/runtimerender/Qt3DSRenderPixelGraphicsRenderer.h54
-rw-r--r--src/runtimerender/Qt3DSRenderPixelGraphicsTypes.cpp66
-rw-r--r--src/runtimerender/Qt3DSRenderPixelGraphicsTypes.h103
-rw-r--r--src/runtimerender/Qt3DSRenderPlugin.cpp936
-rw-r--r--src/runtimerender/Qt3DSRenderPlugin.h148
-rw-r--r--src/runtimerender/Qt3DSRenderPluginCInterface.h330
-rw-r--r--src/runtimerender/Qt3DSRenderPluginGraphObject.h60
-rw-r--r--src/runtimerender/Qt3DSRenderPluginPropertyValue.h182
-rw-r--r--src/runtimerender/Qt3DSRenderProfiler.h112
-rw-r--r--src/runtimerender/Qt3DSRenderRay.cpp164
-rw-r--r--src/runtimerender/Qt3DSRenderRay.h101
-rw-r--r--src/runtimerender/Qt3DSRenderRenderList.cpp124
-rw-r--r--src/runtimerender/Qt3DSRenderRenderList.h125
-rw-r--r--src/runtimerender/Qt3DSRenderRotationHelper.h193
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCache.cpp770
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCache.h160
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCodeGenerator.cpp526
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCodeGenerator.h175
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.cpp671
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCodeGeneratorV2.h140
-rw-r--r--src/runtimerender/Qt3DSRenderShaderKeys.h802
-rw-r--r--src/runtimerender/Qt3DSRenderShadowMap.cpp219
-rw-r--r--src/runtimerender/Qt3DSRenderShadowMap.h183
-rw-r--r--src/runtimerender/Qt3DSRenderSubPresentationHelper.h69
-rw-r--r--src/runtimerender/Qt3DSRenderSubpresentation.cpp127
-rw-r--r--src/runtimerender/Qt3DSRenderSubpresentation.h110
-rw-r--r--src/runtimerender/Qt3DSRenderTaggedPointer.h85
-rw-r--r--src/runtimerender/Qt3DSRenderTessModeValues.h74
-rw-r--r--src/runtimerender/Qt3DSRenderTextTextureAtlas.cpp126
-rw-r--r--src/runtimerender/Qt3DSRenderTextTextureAtlas.h63
-rw-r--r--src/runtimerender/Qt3DSRenderTextTextureCache.cpp289
-rw-r--r--src/runtimerender/Qt3DSRenderTextTextureCache.h74
-rw-r--r--src/runtimerender/Qt3DSRenderTextTypes.h193
-rw-r--r--src/runtimerender/Qt3DSRenderTextureAtlas.cpp364
-rw-r--r--src/runtimerender/Qt3DSRenderTextureAtlas.h102
-rw-r--r--src/runtimerender/Qt3DSRenderThreadPool.cpp277
-rw-r--r--src/runtimerender/Qt3DSRenderThreadPool.h127
-rw-r--r--src/runtimerender/Qt3DSRenderUIPLoader.cpp2089
-rw-r--r--src/runtimerender/Qt3DSRenderUIPLoader.h133
-rw-r--r--src/runtimerender/Qt3DSRenderUIPSharedTranslation.cpp464
-rw-r--r--src/runtimerender/Qt3DSRenderUIPSharedTranslation.h485
-rw-r--r--src/runtimerender/Qt3DSRenderWidgets.cpp319
-rw-r--r--src/runtimerender/Qt3DSRenderWidgets.h181
-rw-r--r--src/runtimerender/Qt3DSRenderableImage.h77
-rw-r--r--src/runtimerender/Qt3DSRenderer.h249
-rw-r--r--src/runtimerender/Qt3DSRendererUtil.cpp119
-rw-r--r--src/runtimerender/Qt3DSRendererUtil.h62
-rw-r--r--src/runtimerender/Qt3DSTextRenderer.cpp87
-rw-r--r--src/runtimerender/Qt3DSTextRenderer.h150
-rw-r--r--src/runtimerender/android/DynamicLibLoader.h30
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp496
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderCamera.h177
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderCustomMaterial.h147
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.cpp74
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.h148
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.cpp160
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderDynamicObject.h113
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderEffect.cpp61
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderEffect.h86
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderGraphObject.h77
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderImage.cpp152
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderImage.h114
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLayer.cpp105
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLayer.h203
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLight.cpp55
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLight.h90
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLightmaps.cpp41
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderLightmaps.h77
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderMaterialDirty.h61
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderModel.cpp75
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderModel.h76
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderNode.cpp499
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderNode.h307
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderPath.cpp59
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderPath.h156
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderPathSubPath.h65
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderPresentation.cpp43
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderPresentation.h94
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderReferencedMaterial.h63
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderScene.cpp122
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderScene.h84
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderText.cpp68
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderText.h76
-rw-r--r--src/runtimerender/linux/DynamicLibLoader.h92
-rw-r--r--src/runtimerender/macos/DynamicLibLoader.h30
-rw-r--r--src/runtimerender/q3dsqmlrender.cpp144
-rw-r--r--src/runtimerender/q3dsqmlrender.h101
-rw-r--r--src/runtimerender/qnx/DynamicLibLoader.h30
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp543
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h472
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp2051
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImpl.h549
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp2220
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h182
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp261
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h115
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp1477
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h367
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp3007
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h452
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h463
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp326
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h85
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp1092
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h119
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp538
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h96
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp715
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h184
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp1262
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp695
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h88
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h413
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp851
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp255
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp277
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h39
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp599
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h129
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp101
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h96
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp436
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h81
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp174
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h126
-rw-r--r--src/runtimerender/windows/DynamicLibLoader.h80
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 &paramName,
+ 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 &paramName,
+ 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 &currentObject = *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 &paramName,
+ 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