all repos — mgba @ 6a4af381511e20c6cd8a462f1070c3292689c238

mGBA Game Boy Advance Emulator

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}