1// Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qskyboxentity.h"
5#include "qskyboxentity_p.h"
6
7#include <QtCore/qtimer.h>
8#include <Qt3DRender/qfilterkey.h>
9#include <Qt3DRender/qeffect.h>
10#include <Qt3DRender/qtexture.h>
11#include <Qt3DRender/qmaterial.h>
12#include <Qt3DRender/qcullface.h>
13#include <Qt3DRender/qdepthtest.h>
14#include <Qt3DRender/qparameter.h>
15#include <Qt3DRender/qtechnique.h>
16#include <Qt3DExtras/qcuboidmesh.h>
17#include <Qt3DRender/qrenderpass.h>
18#include <Qt3DRender/qgraphicsapifilter.h>
19#include <Qt3DRender/qseamlesscubemap.h>
20#include <Qt3DRender/qshaderprogram.h>
21#include <Qt3DRender/qgeometryrenderer.h>
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt3DCore;
26using namespace Qt3DRender;
27
28namespace Qt3DExtras {
29
30QSkyboxEntityPrivate::QSkyboxEntityPrivate()
31 : QEntityPrivate()
32 , m_effect(new QEffect())
33 , m_material(new QMaterial())
34 , m_skyboxTexture(new QTextureCubeMap())
35 , m_loadedTexture(new QTextureLoader())
36 , m_gl3Shader(new QShaderProgram())
37 , m_gl2es2Shader(new QShaderProgram())
38 , m_rhiShader(new QShaderProgram())
39 , m_gl2Technique(new QTechnique())
40 , m_es2Technique(new QTechnique())
41 , m_gl3Technique(new QTechnique())
42 , m_rhiTechnique(new QTechnique())
43 , m_filterKey(new QFilterKey)
44 , m_gl2RenderPass(new QRenderPass())
45 , m_es2RenderPass(new QRenderPass())
46 , m_gl3RenderPass(new QRenderPass())
47 , m_rhiRenderPass(new QRenderPass())
48 , m_mesh(new QCuboidMesh())
49 , m_gammaStrengthParameter(new QParameter(QStringLiteral("gammaStrength"), 0.0f))
50 , m_textureParameter(new QParameter(QStringLiteral("skyboxTexture"), m_skyboxTexture))
51 , m_posXImage(new QTextureImage())
52 , m_posYImage(new QTextureImage())
53 , m_posZImage(new QTextureImage())
54 , m_negXImage(new QTextureImage())
55 , m_negYImage(new QTextureImage())
56 , m_negZImage(new QTextureImage())
57 , m_extension(QStringLiteral(".png"))
58 , m_hasPendingReloadTextureCall(false)
59{
60 m_loadedTexture->setGenerateMipMaps(false);
61}
62
63/*!
64 * \internal
65 */
66void QSkyboxEntityPrivate::init()
67{
68 m_gl3Shader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/gl3/skybox.vert"))));
69 m_gl3Shader->setFragmentShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/gl3/skybox.frag"))));
70 m_gl2es2Shader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/es2/skybox.vert"))));
71 m_gl2es2Shader->setFragmentShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/es2/skybox.frag"))));
72 m_rhiShader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/rhi/skybox.vert"))));
73 m_rhiShader->setFragmentShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/rhi/skybox.frag"))));
74
75 m_gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
76 m_gl3Technique->graphicsApiFilter()->setMajorVersion(3);
77 m_gl3Technique->graphicsApiFilter()->setMinorVersion(3);
78 m_gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
79
80 m_gl2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
81 m_gl2Technique->graphicsApiFilter()->setMajorVersion(2);
82 m_gl2Technique->graphicsApiFilter()->setMinorVersion(0);
83 m_gl2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
84
85 m_es2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
86 m_es2Technique->graphicsApiFilter()->setMajorVersion(2);
87 m_es2Technique->graphicsApiFilter()->setMinorVersion(0);
88 m_es2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
89
90 m_rhiTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
91 m_rhiTechnique->graphicsApiFilter()->setMajorVersion(1);
92 m_rhiTechnique->graphicsApiFilter()->setMinorVersion(0);
93
94 m_filterKey->setParent(m_effect);
95 m_filterKey->setName(QStringLiteral("renderingStyle"));
96 m_filterKey->setValue(QStringLiteral("forward"));
97
98 m_gl3Technique->addFilterKey(filterKey: m_filterKey);
99 m_gl2Technique->addFilterKey(filterKey: m_filterKey);
100 m_es2Technique->addFilterKey(filterKey: m_filterKey);
101 m_rhiTechnique->addFilterKey(filterKey: m_filterKey);
102
103 m_gl3RenderPass->setShaderProgram(m_gl3Shader);
104 m_gl2RenderPass->setShaderProgram(m_gl2es2Shader);
105 m_es2RenderPass->setShaderProgram(m_gl2es2Shader);
106 m_rhiRenderPass->setShaderProgram(m_rhiShader);
107
108 QCullFace *cullFront = new QCullFace();
109 cullFront->setMode(QCullFace::Front);
110 QDepthTest *depthTest = new QDepthTest();
111 depthTest->setDepthFunction(QDepthTest::LessOrEqual);
112 QSeamlessCubemap *seamlessCubemap = new QSeamlessCubemap();
113
114 m_gl3RenderPass->addRenderState(state: cullFront);
115 m_gl3RenderPass->addRenderState(state: depthTest);
116 m_gl3RenderPass->addRenderState(state: seamlessCubemap);
117 m_gl2RenderPass->addRenderState(state: cullFront);
118 m_gl2RenderPass->addRenderState(state: depthTest);
119 m_es2RenderPass->addRenderState(state: cullFront);
120 m_es2RenderPass->addRenderState(state: depthTest);
121 m_rhiRenderPass->addRenderState(state: cullFront);
122 m_rhiRenderPass->addRenderState(state: depthTest);
123
124 m_gl3Technique->addRenderPass(pass: m_gl3RenderPass);
125 m_gl2Technique->addRenderPass(pass: m_gl2RenderPass);
126 m_es2Technique->addRenderPass(pass: m_es2RenderPass);
127 m_rhiTechnique->addRenderPass(pass: m_rhiRenderPass);
128
129 m_effect->addTechnique(t: m_gl3Technique);
130 m_effect->addTechnique(t: m_gl2Technique);
131 m_effect->addTechnique(t: m_es2Technique);
132 m_effect->addTechnique(t: m_rhiTechnique);
133
134 m_material->setEffect(m_effect);
135 m_material->addParameter(parameter: m_gammaStrengthParameter);
136 m_material->addParameter(parameter: m_textureParameter);
137
138 m_mesh->setXYMeshResolution(QSize(2, 2));
139 m_mesh->setXZMeshResolution(QSize(2, 2));
140 m_mesh->setYZMeshResolution(QSize(2, 2));
141
142 m_posXImage->setFace(QTextureCubeMap::CubeMapPositiveX);
143 m_posXImage->setMirrored(false);
144 m_posYImage->setFace(QTextureCubeMap::CubeMapPositiveY);
145 m_posYImage->setMirrored(false);
146 m_posZImage->setFace(QTextureCubeMap::CubeMapPositiveZ);
147 m_posZImage->setMirrored(false);
148 m_negXImage->setFace(QTextureCubeMap::CubeMapNegativeX);
149 m_negXImage->setMirrored(false);
150 m_negYImage->setFace(QTextureCubeMap::CubeMapNegativeY);
151 m_negYImage->setMirrored(false);
152 m_negZImage->setFace(QTextureCubeMap::CubeMapNegativeZ);
153 m_negZImage->setMirrored(false);
154
155 m_skyboxTexture->setMagnificationFilter(QTextureCubeMap::Linear);
156 m_skyboxTexture->setMinificationFilter(QTextureCubeMap::Linear);
157 m_skyboxTexture->setGenerateMipMaps(false);
158 m_skyboxTexture->setWrapMode(QTextureWrapMode(QTextureWrapMode::ClampToEdge));
159
160 m_skyboxTexture->addTextureImage(textureImage: m_posXImage);
161 m_skyboxTexture->addTextureImage(textureImage: m_posYImage);
162 m_skyboxTexture->addTextureImage(textureImage: m_posZImage);
163 m_skyboxTexture->addTextureImage(textureImage: m_negXImage);
164 m_skyboxTexture->addTextureImage(textureImage: m_negYImage);
165 m_skyboxTexture->addTextureImage(textureImage: m_negZImage);
166
167 q_func()->addComponent(comp: m_mesh);
168 q_func()->addComponent(comp: m_material);
169}
170
171/*!
172 * \internal
173 */
174void QSkyboxEntityPrivate::reloadTexture()
175{
176 if (!m_hasPendingReloadTextureCall) {
177 m_hasPendingReloadTextureCall = true;
178 QTimer::singleShot(interval: 0, slot: [this] {
179 if (m_extension == QStringLiteral(".dds")) {
180 m_loadedTexture->setSource(QUrl(m_baseName + m_extension));
181 m_textureParameter->setValue(QVariant::fromValue(value: m_loadedTexture));
182 } else {
183 m_posXImage->setSource(QUrl(m_baseName + QStringLiteral("_posx") + m_extension));
184 m_posYImage->setSource(QUrl(m_baseName + QStringLiteral("_posy") + m_extension));
185 m_posZImage->setSource(QUrl(m_baseName + QStringLiteral("_posz") + m_extension));
186 m_negXImage->setSource(QUrl(m_baseName + QStringLiteral("_negx") + m_extension));
187 m_negYImage->setSource(QUrl(m_baseName + QStringLiteral("_negy") + m_extension));
188 m_negZImage->setSource(QUrl(m_baseName + QStringLiteral("_negz") + m_extension));
189 m_textureParameter->setValue(QVariant::fromValue(value: m_skyboxTexture));
190 }
191 m_hasPendingReloadTextureCall = false;
192 });
193 }
194}
195
196/*!
197 * \class Qt3DExtras::QSkyboxEntity
198 * \inheaderfile Qt3DExtras/QSkyboxEntity
199 * \inmodule Qt3DExtras
200 *
201 * \brief Qt3DExtras::QSkyboxEntity is a convenience Qt3DCore::QEntity subclass that can
202 * be used to insert a skybox in a 3D scene.
203 *
204 * By specifying a base name and an extension, Qt3DExtras::QSkyboxEntity
205 * will take care of building a TextureCubeMap to be rendered at runtime. The
206 * images in the source directory should match the pattern:
207 * \b base name + * "_posx|_posy|_posz|_negx|_negy|_negz" + extension
208 *
209 * By default the extension defaults to .png.
210 *
211 * Be sure to disable frustum culling in the FrameGraph through which the
212 * skybox rendering happens.
213 *
214 * \note Please note that you shouldn't try to render a skybox with an
215 * orthographic projection.
216 *
217 * \since 5.5
218 */
219
220/*!
221 * \qmltype SkyboxEntity
222 * \instantiates Qt3DExtras::QSkyboxEntity
223 * \inqmlmodule Qt3D.Extras
224 *
225 * \brief SkyboxEntity is a convenience Entity subclass that can be used to
226 * insert a skybox in a 3D scene.
227 *
228 * By specifying a base name and an extension, SkyboxEntity will take care of
229 * building a TextureCubeMap to be rendered at runtime. The images in the
230 * source directory should match the pattern: \b base name + *
231 * "_posx|_posy|_posz|_negx|_negy|_negz" + extension
232 *
233 * By default the extension defaults to .png.
234 *
235 * Be sure to disable frustum culling in the FrameGraph through which the
236 * skybox rendering happens.
237 *
238 * \note Please note that you shouldn't try to render a skybox with an
239 * orthographic projection.
240 *
241 * \since 5.5
242 */
243
244/*!
245 * Constructs a new Qt3DExtras::QSkyboxEntity object with \a parent as parent.
246 */
247QSkyboxEntity::QSkyboxEntity(QNode *parent)
248 : QEntity(*new QSkyboxEntityPrivate, parent)
249{
250 d_func()->init();
251}
252
253/*! \internal */
254QSkyboxEntity::~QSkyboxEntity()
255{
256}
257
258/*!
259 * Sets the base name to \a baseName.
260 */
261void QSkyboxEntity::setBaseName(const QString &baseName)
262{
263 Q_D(QSkyboxEntity);
264 if (baseName != d->m_baseName) {
265 d->m_baseName = baseName;
266 emit baseNameChanged(path: baseName);
267 d->reloadTexture();
268 }
269}
270/*!
271 \property QSkyboxEntity::baseName
272
273 Contains the base name of the Skybox.
274*/
275/*!
276 \qmlproperty string QSkyboxEntity::baseName
277
278 Contains the base name of the Skybox.
279*/
280/*!
281 * Returns the base name of the Skybox.
282 */
283QString QSkyboxEntity::baseName() const
284{
285 Q_D(const QSkyboxEntity);
286 return d->m_baseName;
287}
288
289/*!
290 * Sets the extension to \a extension.
291 */
292void QSkyboxEntity::setExtension(const QString &extension)
293{
294 Q_D(QSkyboxEntity);
295 if (extension != d->m_extension) {
296 d->m_extension = extension;
297 emit extensionChanged(extension);
298 d->reloadTexture();
299 }
300}
301
302/*!
303 \property QSkyboxEntity::extension
304
305 Contains the extension of the filename for the skybox image, including the
306 leading '.'.
307
308 The default value is: .png
309*/
310
311/*!
312 \qmlproperty string QSkyboxEntity::extension
313
314 Contains the extension of the filename for the skybox image, including the
315 leading '.'.
316
317 The default value is: .png
318*/
319/*!
320 * Returns the extension
321 */
322QString QSkyboxEntity::extension() const
323{
324 Q_D(const QSkyboxEntity);
325 return d->m_extension;
326}
327
328/*!
329 * Sets the gamma correction enable state to \a enabled.
330 * \since 5.9
331 */
332void QSkyboxEntity::setGammaCorrectEnabled(bool enabled)
333{
334 Q_D(QSkyboxEntity);
335 if (enabled != isGammaCorrectEnabled()) {
336 d->m_gammaStrengthParameter->setValue(enabled ? 1.0f : 0.0f);
337 emit gammaCorrectEnabledChanged(enabled);
338 }
339}
340
341/*!
342 * Returns true if gamma correction is enabled for this skybox.
343 * \since 5.9
344 */
345bool QSkyboxEntity::isGammaCorrectEnabled() const
346{
347 Q_D(const QSkyboxEntity);
348 return !qFuzzyIsNull(f: d->m_gammaStrengthParameter->value().toFloat());
349}
350
351} // namespace Qt3DExtras
352
353/*!
354 \property QSkyboxEntity::gammaCorrect
355
356 A boolean indicating whether gamma correction is enabled.
357 \since 5.9
358*/
359/*!
360 \qmlproperty bool QSkyboxEntity::gammaCorrect
361
362 A boolean indicating whether gamma correction is enabled.
363 \since 5.9
364*/
365QT_END_NAMESPACE
366
367#include "moc_qskyboxentity.cpp"
368

source code of qt3d/src/extras/defaults/qskyboxentity.cpp