src/platform/qt/ShaderSelector.cpp (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ShaderSelector.h"
7
8#include "Display.h"
9#include "VFileDevice.h"
10
11#include <QCheckBox>
12#include <QDoubleSpinBox>
13#include <QFileDialog>
14#include <QFormLayout>
15#include <QGridLayout>
16#include <QSpinBox>
17
18extern "C" {
19#include "platform/video-backend.h"
20
21#if !defined(_WIN32) || defined(USE_EPOXY)
22#include "platform/opengl/gles2.h"
23#endif
24}
25
26using namespace QGBA;
27
28ShaderSelector::ShaderSelector(Display* display, QWidget* parent)
29 : QDialog(parent)
30 , m_display(display)
31{
32 m_ui.setupUi(this);
33
34 refreshShaders();
35
36 connect(m_ui.load, SIGNAL(clicked()), this, SLOT(selectShader()));
37 connect(m_ui.unload, SIGNAL(clicked()), this, SLOT(clearShader()));
38}
39
40ShaderSelector::~ShaderSelector() {
41 clear();
42}
43
44void ShaderSelector::clear() {
45 m_ui.shaderName->setText(tr("No shader active"));
46 m_ui.description->clear();
47 m_ui.author->clear();
48
49 while (QWidget* page = m_ui.passes->widget(0)) {
50 m_ui.passes->removeTab(0);
51 delete page;
52 }
53}
54
55void ShaderSelector::selectShader() {
56#ifdef QT_SHADER_DIR
57 QFileDialog dialog(nullptr, tr("Load shader"), QT_SHADER_DIR, tr("%1 Shader (%.shader)").arg(projectName));
58#else
59 QString path = QCoreApplication::applicationDirPath();
60#ifdef Q_OS_MAC
61 path += QLatin1String("/../Resources");
62#endif
63 path += QLatin1String("/shaders");
64 QFileDialog dialog(nullptr, tr("Load shader"), path, tr("%1 Shader (%.shader)").arg(projectName));
65#endif
66 dialog.setFileMode(QFileDialog::Directory);
67 dialog.exec();
68 QStringList names = dialog.selectedFiles();
69 if (names.count() == 1) {
70 loadShader(names[0]);
71 refreshShaders();
72 }
73}
74
75void ShaderSelector::loadShader(const QString& path) {
76 VDir* shader = VFileDevice::openDir(path);
77 if (!shader) {
78 shader = VFileDevice::openArchive(path);
79 }
80 if (!shader) {
81 return;
82 }
83 m_display->setShaders(shader);
84 shader->close(shader);
85 // TODO: Config
86}
87
88void ShaderSelector::clearShader() {
89 m_display->clearShaders();
90 refreshShaders();
91 // TODO: Config
92}
93
94void ShaderSelector::refreshShaders() {
95 clear();
96 m_shaders = m_display->shaders();
97 if (!m_shaders) {
98 return;
99 }
100 if (m_shaders->name) {
101 m_ui.shaderName->setText(m_shaders->name);
102 } else {
103 m_ui.shaderName->setText(tr("No shader loaded"));
104 }
105 if (m_shaders->description) {
106 m_ui.description->setText(m_shaders->description);
107 } else {
108 m_ui.description->clear();
109 }
110 if (m_shaders->author) {
111 m_ui.author->setText(tr("by %1").arg(m_shaders->author));
112 } else {
113 m_ui.author->clear();
114 }
115
116#if !defined(_WIN32) || defined(USE_EPOXY)
117 m_ui.passes->addTab(makePage(static_cast<GBAGLES2Shader*>(m_shaders->preprocessShader)), tr("Preprocessing"));
118 GBAGLES2Shader* shaders = static_cast<GBAGLES2Shader*>(m_shaders->passes);
119 for (size_t p = 0; p < m_shaders->nPasses; ++p) {
120 QWidget* page = makePage(&shaders[p]);
121 if (page) {
122 m_ui.passes->addTab(page, tr("Pass %1").arg(p + 1));
123 }
124 }
125#endif
126}
127
128void ShaderSelector::addUniform(QGridLayout* settings, float* value, float min, float max, int y, int x) {
129 QDoubleSpinBox* f = new QDoubleSpinBox;
130 f->setDecimals(3);
131 if (min < max) {
132 f->setMinimum(min);
133 f->setMaximum(max);
134 }
135 f->setValue(*value);
136 f->setSingleStep(0.001);
137 f->setAccelerated(true);
138 settings->addWidget(f, y, x);
139 connect(f, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [value](double v) {
140 *value = v;
141 // TODO: Config
142 });
143}
144
145void ShaderSelector::addUniform(QGridLayout* settings, int* value, int min, int max, int y, int x) {
146 QSpinBox* i = new QSpinBox;
147 if (min < max) {
148 i->setMinimum(min);
149 i->setMaximum(max);
150 }
151 i->setValue(*value);
152 i->setSingleStep(1);
153 i->setAccelerated(true);
154 settings->addWidget(i, y, x);
155 connect(i, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [value](int v) {
156 *value = v;
157 // TODO: Config
158 });
159}
160
161QWidget* ShaderSelector::makePage(GBAGLES2Shader* shader) {
162 if (!shader->nUniforms) {
163 return nullptr;
164 }
165 QWidget* page = new QWidget;
166 QFormLayout* layout = new QFormLayout;
167 page->setLayout(layout);
168 for (size_t u = 0 ; u < shader->nUniforms; ++u) {
169 QGridLayout* settings = new QGridLayout;
170 GBAGLES2Uniform* uniform = &shader->uniforms[u];
171 switch (uniform->type) {
172 case GL_FLOAT:
173 addUniform(settings, &uniform->value.f, uniform->min.f, uniform->max.f, 0, 0);
174 break;
175 case GL_FLOAT_VEC2:
176 addUniform(settings, &uniform->value.fvec2[0], uniform->min.fvec2[0], uniform->max.fvec2[0], 0, 0);
177 addUniform(settings, &uniform->value.fvec2[1], uniform->min.fvec2[1], uniform->max.fvec2[1], 0, 1);
178 break;
179 case GL_FLOAT_VEC3:
180 addUniform(settings, &uniform->value.fvec3[0], uniform->min.fvec3[0], uniform->max.fvec3[0], 0, 0);
181 addUniform(settings, &uniform->value.fvec3[1], uniform->min.fvec3[1], uniform->max.fvec3[1], 0, 1);
182 addUniform(settings, &uniform->value.fvec3[2], uniform->min.fvec3[2], uniform->max.fvec3[2], 0, 2);
183 break;
184 case GL_FLOAT_VEC4:
185 addUniform(settings, &uniform->value.fvec4[0], uniform->min.fvec4[0], uniform->max.fvec4[0], 0, 0);
186 addUniform(settings, &uniform->value.fvec4[1], uniform->min.fvec4[1], uniform->max.fvec4[1], 0, 1);
187 addUniform(settings, &uniform->value.fvec4[2], uniform->min.fvec4[2], uniform->max.fvec4[2], 0, 2);
188 addUniform(settings, &uniform->value.fvec4[3], uniform->min.fvec4[3], uniform->max.fvec4[3], 0, 3);
189 break;
190 case GL_INT:
191 addUniform(settings, &uniform->value.i, uniform->min.i, uniform->max.i, 0, 0);
192 break;
193 case GL_INT_VEC2:
194 addUniform(settings, &uniform->value.ivec2[0], uniform->min.ivec2[0], uniform->max.ivec2[0], 0, 0);
195 addUniform(settings, &uniform->value.ivec2[1], uniform->min.ivec2[1], uniform->max.ivec2[1], 0, 1);
196 break;
197 case GL_INT_VEC3:
198 addUniform(settings, &uniform->value.ivec3[0], uniform->min.ivec3[0], uniform->max.ivec3[0], 0, 0);
199 addUniform(settings, &uniform->value.ivec3[1], uniform->min.ivec3[1], uniform->max.ivec3[1], 0, 1);
200 addUniform(settings, &uniform->value.ivec3[2], uniform->min.ivec3[2], uniform->max.ivec3[2], 0, 2);
201 break;
202 case GL_INT_VEC4:
203 addUniform(settings, &uniform->value.ivec4[0], uniform->min.ivec4[0], uniform->max.ivec4[0], 0, 0);
204 addUniform(settings, &uniform->value.ivec4[1], uniform->min.ivec4[1], uniform->max.ivec4[1], 0, 1);
205 addUniform(settings, &uniform->value.ivec4[2], uniform->min.ivec4[2], uniform->max.ivec4[2], 0, 2);
206 addUniform(settings, &uniform->value.ivec4[3], uniform->min.ivec4[3], uniform->max.ivec4[3], 0, 3);
207 break;
208 }
209 layout->addRow(shader->uniforms[u].readableName, settings);
210 }
211 return page;
212}