Skip to content

Commit 8abe67b

Browse files
committed
refactored normals interface.
1 parent 3705b75 commit 8abe67b

14 files changed

+565
-142
lines changed

relightlab/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ set (RELIGHT_HEADERS
8383
queueitem.h
8484
normalsframe.h
8585
normalstask.h
86+
normalsplan.h
87+
planrow.h
8688
scaleframe.h
8789
)
8890

@@ -149,6 +151,8 @@ set (RELIGHTLAB_SOURCES
149151
queueitem.cpp
150152
normalsframe.cpp
151153
normalstask.cpp
154+
normalsplan.cpp
155+
planrow.cpp
152156
scaleframe.cpp
153157
)
154158

relightlab/normalsframe.cpp

+51-24
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "processqueue.h"
55
#include "helpbutton.h"
66
#include "../src/project.h"
7+
#include "normalsplan.h"
78

89
#include <QVBoxLayout>
910
#include <QLabel>
@@ -21,6 +22,52 @@ NormalsFrame::NormalsFrame(QWidget *parent): QFrame(parent) {
2122
content->addWidget(new QLabel("<h2>Export normals</h2>"));
2223
content->addSpacing(30);
2324

25+
26+
content->addWidget(source_row = new NormalsSourceRow(parameters, this));
27+
content->addWidget(flatten_row = new NormalsFlattenRow(parameters, this));
28+
content->addWidget(surface_row = new NormalsSurfaceRow(parameters, this));
29+
30+
31+
{
32+
QHBoxLayout *save_row = new QHBoxLayout;
33+
34+
{
35+
QLabel *label = new QLabel("");
36+
label->setFixedWidth(200);
37+
save_row->addWidget(label, 0, Qt::AlignLeft);
38+
}
39+
save_row->addStretch(1);
40+
41+
{
42+
QFrame *buttons_frame = new QFrame;
43+
buttons_frame->setMinimumWidth(860);
44+
45+
{
46+
QHBoxLayout *buttons_layout = new QHBoxLayout(buttons_frame);
47+
48+
buttons_layout->addStretch(1);
49+
QPushButton *save = new QPushButton("Export", this);
50+
save->setIcon(QIcon::fromTheme("save"));
51+
save->setProperty("class", "large");
52+
save->setMinimumWidth(200);
53+
connect(save, &QPushButton::clicked, [this]() { this->save(); });
54+
55+
buttons_layout->addWidget(save);
56+
}
57+
58+
save_row->addWidget(buttons_frame);
59+
}
60+
save_row->addStretch(1);
61+
62+
63+
content->addLayout(save_row);
64+
65+
}
66+
67+
68+
content->addStretch();
69+
70+
return;
2471
content->addWidget(jpg = new QRadioButton("JPEG: normalmap"));
2572
content->addWidget(png = new QRadioButton("PNG: normalmap"));
2673
{
@@ -69,35 +116,15 @@ NormalsFrame::NormalsFrame(QWidget *parent): QFrame(parent) {
69116
}
70117

71118
void NormalsFrame::save() {
72-
if(qRelightApp->project().dome.directions.size() == 0) {
119+
if(parameters.compute && qRelightApp->project().dome.directions.size() == 0) {
73120
QMessageBox::warning(this, "Missing light directions.", "You need light directions for this dataset to build a normalmap.\n"
74121
"You can either load a dome or .lp file or mark a reflective sphere in the 'Lights' tab.");
75122
return;
76123
}
77-
QString filter = jpg->isChecked() ? "JPEG Images (*.jpg)" : "PNG Images (*.png)";
78-
Project &project = qRelightApp->project();
79-
QString output = QFileDialog::getSaveFileName(this, "Select a filename for the normal map.", project.dir.path(), filter);
80-
if(output.isNull())
81-
return;
82-
QString extension = jpg->isChecked() ? ".jpg" : ".png";
83-
if(!output.endsWith(extension))
84-
output += extension;
85-
86-
NormalsTask *task = new NormalsTask(output);
87-
if(ply->isChecked())
88-
task->exportPly = true;
89-
if(tif->isChecked())
90-
task->exportTiff = true;
91-
92-
if(radial->isChecked())
93-
task->flatMethod = RADIAL;
94-
95-
if(fourier->isChecked()) {
96-
task->flatMethod = FOURIER;
97-
task->m_FlatRadius = fourier_radius->value()/100.0f;
98-
}
99124

100-
task->initFromProject(project);
125+
NormalsTask *task = new NormalsTask();
126+
task->parameters = parameters;
127+
task->initFromProject(qRelightApp->project());
101128

102129
ProcessQueue &queue = ProcessQueue::instance();
103130
queue.addTask(task);

relightlab/normalsframe.h

+11
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22
#define NORMALSFRAME_H
33

44
#include <QFrame>
5+
#include "normalstask.h"
56

67
class QCheckBox;
78
class QRadioButton;
89
class QSpinBox;
910
class QDoubleSpinBox;
1011

12+
class NormalsSourceRow;
13+
class NormalsFlattenRow;
14+
class NormalsSurfaceRow;
15+
class NormalsExportRow;
1116
class NormalsFrame: public QFrame {
1217
Q_OBJECT
1318
public:
1419
NormalsFrame(QWidget *parent = nullptr);
20+
NormalsParameters parameters;
1521

1622
public slots:
1723
void save();
@@ -20,6 +26,11 @@ public slots:
2026
void processStarted();
2127

2228
private:
29+
NormalsSourceRow *source_row = nullptr;
30+
NormalsFlattenRow *flatten_row = nullptr;
31+
NormalsSurfaceRow *surface_row = nullptr;
32+
NormalsExportRow *export_row = nullptr;
33+
2334
QRadioButton *jpg = nullptr;
2435
QRadioButton *png = nullptr;
2536
QCheckBox *tif = nullptr;

relightlab/normalsplan.cpp

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#include "normalsplan.h"
2+
#include "qlabelbutton.h"
3+
#include "helpbutton.h"
4+
#include "relightapp.h"
5+
6+
#include <QButtonGroup>
7+
#include <QPushButton>
8+
#include <QLineEdit>
9+
#include <QHBoxLayout>
10+
#include <QDoubleSpinBox>
11+
#include <QMessageBox>
12+
#include <QFileDialog>
13+
14+
NormalsPlanRow::NormalsPlanRow(NormalsParameters &_parameters, QFrame *parent):
15+
PlanRow(parent), parameters(_parameters) {
16+
17+
}
18+
19+
20+
NormalsSourceRow::NormalsSourceRow(NormalsParameters &_parameters, QFrame *parent):
21+
NormalsPlanRow(_parameters, parent) {
22+
label->label->setText("Source:");
23+
label->help->setId("normals/normalmap");
24+
25+
compute = new QLabelButton("Compute", "Compute normals from images");
26+
file = new QLabelButton("Normalmap", "Loat a normalmap image.");
27+
buttons->addWidget(compute, 0, Qt::AlignCenter);
28+
29+
QVBoxLayout *button_layout =new QVBoxLayout;
30+
button_layout->addWidget(file);
31+
32+
QHBoxLayout *loader_layout = new QHBoxLayout;
33+
loader_layout->addWidget(path = new QLineEdit);
34+
loader_layout->addWidget(open = new QPushButton("..."));
35+
36+
button_layout->addLayout(loader_layout);
37+
38+
39+
buttons->addLayout(button_layout, 0); //, Qt::AlignCenter);
40+
41+
connect(compute, &QAbstractButton::clicked, this, [this](){ setComputeSource(true); });
42+
connect(file, &QAbstractButton::clicked, this, [this](){ setComputeSource(false); });
43+
//connect(rbf, &QAbstractButton::clicked, this, [this](){ setBasis(Rti::RBF, true); });
44+
//connect(bln, &QAbstractButton::clicked, this, [this](){ setBasis(Rti::BILINEAR, true); });
45+
46+
QButtonGroup *group = new QButtonGroup(this);
47+
48+
group->addButton(compute);
49+
group->addButton(file);
50+
}
51+
52+
53+
void NormalsSourceRow::setComputeSource(bool compute) {
54+
parameters.compute = compute;
55+
}
56+
57+
58+
59+
NormalsFlattenRow::NormalsFlattenRow(NormalsParameters &_parameters, QFrame *parent):
60+
NormalsPlanRow(_parameters, parent) {
61+
label->label->setText("Flatten:");
62+
label->help->setId("normals/flattening");
63+
64+
none = new QLabelButton("None", "Do not flatten the surface");
65+
radial = new QLabelButton("Radial", "Polynomial radial fitting.");
66+
fourier = new QLabelButton("Fourier", "Remove low frequencies");
67+
68+
buttons->addWidget(none, 1, Qt::AlignCenter);
69+
buttons->addWidget(radial, 1, Qt::AlignCenter);
70+
71+
QVBoxLayout *button_layout =new QVBoxLayout;
72+
button_layout->addWidget(fourier);
73+
74+
QHBoxLayout *loader_layout = new QHBoxLayout;
75+
loader_layout->addWidget(new QLabel("Fourier low pass frequency."));
76+
loader_layout->addWidget(max_frequency = new QDoubleSpinBox);
77+
max_frequency->setRange(0.01, 1);
78+
79+
button_layout->addLayout(loader_layout);
80+
81+
82+
buttons->addLayout(button_layout, 1); //, Qt::AlignCenter);
83+
84+
connect(none, &QAbstractButton::clicked, this, [this](){ setFlattenMethod(FLAT_NONE); });
85+
connect(radial, &QAbstractButton::clicked, this, [this](){ setFlattenMethod(FLAT_RADIAL); });
86+
connect(fourier, &QAbstractButton::clicked, this, [this](){ setFlattenMethod(FLAT_FOURIER); });
87+
88+
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
89+
connect(max_frequency, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.m_FlatRadius = v; });
90+
#else
91+
connect(max_frequency, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.m_FlatRadius = v; });
92+
#endif
93+
94+
95+
QButtonGroup *group = new QButtonGroup(this);
96+
97+
group->addButton(none);
98+
group->addButton(radial);
99+
group->addButton(fourier);
100+
}
101+
102+
void NormalsFlattenRow::setFlattenMethod(FlatMethod method) {
103+
parameters.flatMethod = method;
104+
none->setChecked(method == FLAT_NONE);
105+
radial->setChecked(method == FLAT_RADIAL);
106+
fourier->setChecked(method == FLAT_FOURIER);
107+
}
108+
109+
void NormalsFlattenRow::setFourierFrequency(double f) {
110+
parameters.m_FlatRadius = f;
111+
max_frequency->setValue(f);
112+
}
113+
114+
115+
116+
NormalsSurfaceRow::NormalsSurfaceRow(NormalsParameters &_parameters, QFrame *parent):
117+
NormalsPlanRow(_parameters, parent) {
118+
label->label->setText("Flatten:");
119+
label->help->setId("normals/flattening");
120+
121+
none = new QLabelButton("None", "Do generate a mesh.");
122+
bni = new QLabelButton("Bilateral Normal Integration", "Dense, allows discontinuity");
123+
assm = new QLabelButton("Adaptive Surface Meshing.", "Adaptive, no discontinuities.");
124+
125+
buttons->addWidget(none, 0, Qt::AlignCenter);
126+
127+
{
128+
QVBoxLayout *bni_layout =new QVBoxLayout;
129+
bni_layout->addWidget(bni);
130+
{
131+
QHBoxLayout *bni_parameter = new QHBoxLayout;
132+
bni_parameter->addWidget(new QLabel("Discontinuity propensity."));
133+
bni_parameter->addWidget(bni_k = new QDoubleSpinBox);
134+
bni_k->setRange(0.01, 50);
135+
bni_k->setValue(parameters.bni_k);
136+
137+
bni_layout->addLayout(bni_parameter);
138+
}
139+
buttons->addLayout(bni_layout, 0); //, Qt::AlignCenter);
140+
}
141+
142+
{
143+
QVBoxLayout *assm_layout =new QVBoxLayout;
144+
assm_layout->addWidget(assm);
145+
146+
{
147+
QHBoxLayout *assm_parameter = new QHBoxLayout;
148+
assm_parameter->addWidget(new QLabel("Mesh error in pixels.."));
149+
assm_parameter->addWidget(assm_error = new QDoubleSpinBox);
150+
assm_error->setRange(0.001, 100);
151+
assm_error->setValue(parameters.assm_error);
152+
153+
assm_layout->addLayout(assm_parameter);
154+
}
155+
buttons->addLayout(assm_layout, 0); //, Qt::AlignCenter);
156+
}
157+
158+
connect(none, &QAbstractButton::clicked, this, [this](){ setSurfaceMethod(SURFACE_NONE); });
159+
connect(bni, &QAbstractButton::clicked, this, [this](){ setSurfaceMethod(SURFACE_BNI); });
160+
connect(assm, &QAbstractButton::clicked, this, [this](){ setSurfaceMethod(SURFACE_ASSM); });
161+
162+
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
163+
connect(bni_k, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.bni_k = v; });
164+
connect(assm_error, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.assm_error = v; });
165+
#else
166+
connect(bni_k, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.bni_k = v; });
167+
connect(assm_error, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [this](double v) { parameters.assm_error = v; });
168+
#endif
169+
170+
QButtonGroup *group = new QButtonGroup(this);
171+
group->addButton(none);
172+
group->addButton(bni);
173+
group->addButton(assm);
174+
}
175+
176+
void NormalsSurfaceRow::setSurfaceMethod(SurfaceIntegration surface) {
177+
parameters.surface_integration = surface;
178+
none->setChecked(surface == SURFACE_NONE);
179+
bni->setChecked(surface == SURFACE_BNI);
180+
assm->setChecked(surface == SURFACE_ASSM);
181+
}
182+
183+
184+
185+
NormalsExportRow::NormalsExportRow(NormalsParameters &parameters, QFrame *parent): NormalsPlanRow(parameters, parent) {
186+
label->label->setText("Directory/File:");
187+
label->help->setId("normals/export");
188+
189+
path_edit = new QLineEdit;
190+
connect(path_edit, &QLineEdit::editingFinished,this, &NormalsExportRow::verifyPath);
191+
buttons->addWidget(path_edit);
192+
QPushButton *path_button = new QPushButton("...");
193+
buttons->addWidget(path_button);
194+
connect(path_button, &QPushButton::clicked, this, &NormalsExportRow::selectOutput);
195+
}
196+
197+
void NormalsExportRow::setPath(QString path, bool emitting) {
198+
path_edit->setText(path);
199+
parameters.path = path;
200+
}
201+
202+
void NormalsExportRow::verifyPath() {
203+
parameters.path = QString();
204+
QString path = path_edit->text();
205+
QDir path_dir(path);
206+
path_dir.cdUp();
207+
if(!path_dir.exists()) {
208+
QMessageBox::warning(this, "Invalid output path", "The specified path is not valid");
209+
return;
210+
}
211+
if(!path.endsWith(".jpg") && !path.endsWith(".png")) {
212+
path += ".jpg";
213+
path_edit->setText(path);
214+
}
215+
parameters.path = path;
216+
}
217+
218+
void NormalsExportRow::selectOutput() {
219+
//get folder if not legacy.
220+
QString output_parent = qRelightApp->lastOutputDir();
221+
222+
QString output = QFileDialog::getSaveFileName(this, "Select a file name", output_parent);
223+
if(output.isNull()) return;
224+
225+
if(!output.endsWith(".jpg") && !output.endsWith(".png"))
226+
output += ".jpg";
227+
228+
QDir output_parent_dir(output);
229+
output_parent_dir.cdUp();
230+
qRelightApp->setLastOutputDir(output_parent_dir.absolutePath());
231+
setPath(output);
232+
}
233+
234+
void NormalsExportRow::suggestPath() {
235+
QDir input = qRelightApp->project().dir;
236+
input.cdUp();
237+
QString filename = input.filePath("normals");
238+
setPath(filename);
239+
}

0 commit comments

Comments
 (0)