1 /****************************************************************************
2 * MeshLab o o *
3 * A versatile mesh processing toolbox o o *
4 * _ O _ *
5 * Copyright(C) 2005-2008 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23 /****************************************************************************
24 History
25 $Log$
26 Revision 1.10 2007/12/11 16:21:06 corsini
27 improve textures parameters description
28
29 Revision 1.9 2007/12/10 14:22:06 corsini
30 new version with layout correct
31
32 Revision 1.7 2007/12/06 14:47:35 corsini
33 remove model reference
34
35 Revision 1.6 2007/12/03 11:56:10 corsini
36 code restyling
37
38
39 ****************************************************************************/
40
41 // Local headers
42 #include "rmshaderdialog.h"
43
44
45 // we create the dialog with all the proper contents
RmShaderDialog(GLStateHolder * _holder,RmXmlParser * _parser,QGLWidget * gla,RenderMode & rm,QWidget * parent)46 RmShaderDialog::RmShaderDialog(GLStateHolder * _holder, RmXmlParser * _parser,
47 QGLWidget* gla, RenderMode &rm, QWidget *parent)
48 : QDialog(parent)
49 {
50 ui.setupUi(this);
51
52 // make this dialog always visible
53 this->setWindowFlags(Qt::WindowStaysOnTopHint);
54
55 // customize the layout
56 layoutUniform = dynamic_cast<QGridLayout *>(ui.frameUniform->layout());
57 layoutTextures = dynamic_cast<QGridLayout *>(ui.frameTextures->layout());
58 layoutOpengl = dynamic_cast<QGridLayout *>(ui.frameOpenGL->layout());
59
60 parser = _parser;
61 holder = _holder;
62 glarea = gla;
63 rendMode = &rm;
64
65 eff_selected = NULL;
66 pass_selected = NULL;
67
68 if(parser->size() == 0) {
69 QMessageBox::critical(0, "Meshlab",
70 QString("This RmShader seems to have no suitable effects"));
71 return;
72 }
73
74 // fill the effect combo
75 for (int i = 0; i < parser->size(); i++)
76 ui.cmbEffectSelection->addItem(parser->at(i).getName());
77
78 // signal-slot connections
79 connect(ui.cmbEffectSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(fillDialogWithEffect(int)));
80 connect(ui.cmbPassSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(fillTabsWithPass(int)));
81 connect(ui.btnOk, SIGNAL(clicked()), this, SLOT(accept()));
82
83 signaler = NULL;
84
85 fillDialogWithEffect(0);
86 }
87
~RmShaderDialog()88 RmShaderDialog::~RmShaderDialog()
89 {
90 for (int i = 0; i < shown.size(); i++)
91 delete shown[i];
92
93 delete signaler;
94 }
95
fillDialogWithEffect(int index)96 void RmShaderDialog::fillDialogWithEffect(int index)
97 {
98 if (index < 0 || index >= parser -> size())
99 return;
100
101 eff_selected = &(parser->at(index));
102 ui.cmbPassSelection->clear();
103
104 for (int i = 0; i < eff_selected->size(); i++)
105 ui.cmbPassSelection->addItem(eff_selected->at(i).getName());
106
107 holder->setPasses(eff_selected->getPassList());
108 fillTabsWithPass(0);
109
110 if (!holder->compile()) {
111 QMessageBox::critical(0, "Meshlab",
112 "An error occurred during shader compiling.\n" + holder->getLastError());
113 } else if (!holder->link()) {
114 QMessageBox::critical(0, "Meshlab",
115 "An error occurred during shader linking.\n" + holder->getLastError());
116 }
117 }
118
fillTabsWithPass(int index)119 void RmShaderDialog::fillTabsWithPass(int index)
120 {
121 clearTabs();
122 if (index < 0 || eff_selected == NULL || index >= eff_selected->size())
123 return;
124
125 pass_selected = &(eff_selected->at(index));
126
127 // Set the source code of vertex shader
128 ui.textVertex->setText(pass_selected->getVertex());
129
130 // Set the source code of fragment shader
131 ui.textFragment->setText(pass_selected->getFragment());
132
133 // General Info in the first tab
134 QString info;
135 if (pass_selected->hasRenderTarget())
136 info += "Render Target: " + pass_selected->getRenderTarget().name + "\n";
137
138 for (int i = 0; i < 2; i++)
139 for (int j = 0;
140 j < (i == 0 ? pass_selected->vertexUniformVariableSize() : pass_selected->fragmentUniformVariableSize());
141 j++) {
142 UniformVar v = pass_selected->getUniform(j, i == 0 ? RmPass::VERTEX : RmPass::FRAGMENT);
143
144 if(v.representerTagName == "RmRenderTarget") {
145 if (i == 0)
146 info += "Vertex";
147 else
148 info += "Fragment";
149
150 info += " render Input: " + v.name;
151
152 for (int k = 0; k < eff_selected -> size(); k++)
153 if (eff_selected->at(k).getRenderTarget().name == v.textureName) {
154 info += " (from pass: " + eff_selected->at(k).getName() + ")";
155 break;
156 }
157
158 info += "\n";
159 }
160 }
161
162 if (!info.isEmpty()) {
163 QLabel *lblinfo = new QLabel(info);
164 layoutUniform->addWidget(lblinfo, 0, 0, 1, 5);
165 shown.append(lblinfo);
166 }
167
168 // any value change is sent to the state holder with this mapper
169 // Signal are send from signaler in the form "varnameNM" where
170 // NM is the index of row and column in case of matrix. (00 if
171 // it is a simple variable).
172 delete signaler;
173 signaler = new QSignalMapper();
174 connect(signaler, SIGNAL(mapped(const QString &)), this, SLOT(valuesChanged(const QString &)));
175
176
177 // Uniform Variables in the first Tab
178 QList<QString> usedVarables; // parser can give same variable twice in the vertex and fragment
179 int row = 1;
180 for (int ii = 0; ii < 2; ii++)
181 for (int jj = 0;
182 jj < (ii == 0 ? pass_selected->vertexUniformVariableSize() : pass_selected->fragmentUniformVariableSize());
183 jj++) {
184 UniformVar v = pass_selected->getUniform(jj, ii == 0 ? RmPass::VERTEX : RmPass::FRAGMENT);
185 if (v.representerTagName == "RmRenderTarget" || usedVarables.contains(v.name))
186 continue;
187 usedVarables.append(v.name);
188
189 QString varname = (ii == 0 ? "Vertex: " : "Fragment: ");
190 varname += UniformVar::getStringFromUniformType(v.type) +
191 " " + v.name + (v.minSet || v.maxSet ? "\n" : "");
192
193 switch (v.type) {
194 case UniformVar::INT:
195 case UniformVar::IVEC2:
196 case UniformVar::IVEC3:
197 case UniformVar::IVEC4: {
198 int n = v.type == UniformVar::INT ? 1 : (v.type == UniformVar::IVEC2 ? 2 : (v.type == UniformVar::IVEC3 ? 3 : 4 ));
199 for (int i = 0; i < n; i++) {
200 QSpinBox *input = new QSpinBox();
201 input->setObjectName(v.name + "0" + QString().setNum(i));
202 if (v.minSet)
203 input->setMinimum(v.fmin);
204 else
205 input -> setMinimum(-1000);
206
207 if (v.maxSet)
208 input->setMaximum(v.fmax);
209 else
210 input->setMaximum(1000);
211
212 input->setSingleStep((v.minSet && v.maxSet )? std::max(( v.imax - v.imin )/10, 1) : 1 );
213 input->setValue(v.ivec4[i]);
214 layoutUniform->addWidget(input, row, 1 + i, 1,
215 ((i + 1)==n ? 5-n : 1));
216 shown.append(input);
217
218 connect(input, SIGNAL(valueChanged(int)), signaler, SLOT(map()));
219 signaler->setMapping(input, v.name + "0" + QString().setNum(i));
220 }
221 if (v.minSet) {
222 varname += "min: " + QString().setNum(v.imin) + " ";
223 }
224 if (v.maxSet) {
225 varname += " max: " + QString().setNum(v.imax);
226 }
227 break;
228 }
229 case UniformVar::BOOL:
230 case UniformVar::BVEC2:
231 case UniformVar::BVEC3:
232 case UniformVar::BVEC4:
233 {
234 int n = v.type == UniformVar::BOOL ? 1 : (v.type == UniformVar::BVEC2 ? 2 : (v.type == UniformVar::BVEC3 ? 3 : 4 ));
235 for( int i = 0; i < n; i++ ) {
236 QCheckBox * input = new QCheckBox();
237 input -> setObjectName( v.name + "0" + QString().setNum(i) );
238 input -> setCheckState( v.bvec4[i] ? Qt::Checked : Qt::Unchecked );
239 layoutUniform->addWidget(input, row, 1+i, 1, ((i+1)==n ? 5-n : 1));
240 shown.append(input);
241
242 connect(input, SIGNAL(stateChanged(int)), signaler, SLOT(map()));
243 signaler->setMapping(input, v.name + "0" + QString().setNum(i) );
244 }
245 break;
246 }
247 case UniformVar::FLOAT:
248 case UniformVar::VEC2:
249 case UniformVar::VEC3:
250 case UniformVar::VEC4:
251 {
252 int n = v.type == UniformVar::FLOAT ? 1 : (v.type == UniformVar::VEC2 ? 2 : (v.type == UniformVar::VEC3 ? 3 : 4 ));
253 for( int i = 0; i < n; i++ )
254 {
255 QDoubleSpinBox * input = new QDoubleSpinBox();
256 input -> setObjectName( v.name + "0" + QString().setNum(i) );
257 input -> setDecimals(4);
258 if( v.minSet ) input -> setMinimum( v.fmin ); else input -> setMinimum( -1000 );
259 if( v.maxSet ) input -> setMaximum( v.fmax ); else input -> setMaximum( 1000 );
260 input -> setSingleStep( (v.minSet && v.maxSet ) ? std::max(( v.fmax - v.fmin )/10., 0.0001) : 0.0001 );
261 input -> setValue( v.vec4[i] );
262 layoutUniform->addWidget( input, row, 1+i, 1, ((i+1)==n ? 5-n : 1) );
263 shown.append(input);
264
265 connect(input, SIGNAL(valueChanged(double)), signaler, SLOT(map()));
266 signaler->setMapping(input, v.name + "0" + QString().setNum(i) );
267 }
268 if( v.minSet ) { varname += "min: " + QString().setNum(v.fmin) + " "; }
269 if( v.maxSet ) { varname += " max: " + QString().setNum(v.fmax); }
270 break;
271 }
272 case UniformVar::MAT2:
273 case UniformVar::MAT3:
274 case UniformVar::MAT4:
275 {
276 int n = v.type == UniformVar::MAT2 ? 2 : (v.type == UniformVar::MAT3 ? 3 : 4 );
277 for( int i = 0; i < n; i++ ) {
278 for( int j = 0; j < n; j++ ) {
279 QDoubleSpinBox * input = new QDoubleSpinBox();
280 input -> setObjectName( v.name + QString().setNum(i) + QString().setNum(j));
281 input -> setDecimals(4);
282 if( v.minSet ) input -> setMinimum( v.fmin ); else input -> setMinimum( -1000 );
283 if( v.maxSet ) input -> setMaximum( v.fmax ); else input -> setMaximum( 1000 );
284 input -> setSingleStep( (v.minSet && v.maxSet ) ? std::max(( v.fmax - v.fmin )/10., 0.0001) : 0.0001 );
285 input -> setValue( v.vec4[(i*n)+j] );
286 layoutUniform->addWidget(input, row, 1+j, 1, ((j+1)==n ? 5-n : 1));
287 shown.append(input);
288
289 connect(input, SIGNAL(valueChanged(double)), signaler, SLOT(map()));
290 signaler->setMapping(input, v.name + QString().setNum(i) + QString().setNum(j));
291 }
292 if( (i+1) < n ) row += 1;
293 }
294 if( v.minSet ) { varname += "min: " + QString().setNum(v.fmin) + " "; }
295 if( v.maxSet ) { varname += " max: " + QString().setNum(v.fmax); }
296 break;
297 }
298 case UniformVar::SAMPLER1D:
299 case UniformVar::SAMPLER2D:
300 case UniformVar::SAMPLER3D:
301 case UniformVar::SAMPLERCUBE:
302 case UniformVar::SAMPLER1DSHADOW:
303 case UniformVar::SAMPLER2DSHADOW:
304 {
305 QLabel * link = new QLabel( "<font color=\"blue\">See texture tab</font>" );
306 layoutUniform->addWidget(link, row, 1, 1, 4);
307 shown.append(link);
308 break;
309 }
310 case UniformVar::OTHER:
311 {
312 QLabel * unimpl = new QLabel( "[Unimplemented mask]" );
313 layoutUniform->addWidget( unimpl, row, 1, 1, 4);
314 shown.append(unimpl);
315 }
316 }
317
318 QLabel * lblvar = new QLabel(varname);
319 layoutUniform->addWidget( lblvar, row, 0 );
320 shown.append(lblvar);
321
322 row += 1;
323 }
324
325
326 // Texture in the second tab
327 for( int ii = 0, row = 0; ii < 2; ii++ )
328 for( int jj = 0; jj < ( ii == 0 ? pass_selected -> vertexUniformVariableSize() : pass_selected -> fragmentUniformVariableSize()); jj++ )
329 {
330 UniformVar v = pass_selected -> getUniform( jj, ii == 0 ? RmPass::VERTEX : RmPass::FRAGMENT );
331 if( v.textureFilename.isNull() ) continue;
332
333 QFileInfo finfo(v.textureFilename);
334
335 QDir textureDir = QDir(qApp->applicationDirPath());
336 #if defined(Q_OS_WIN)
337 if (textureDir.dirName() == "debug" || textureDir.dirName() == "release" || textureDir.dirName() == "plugins" ) textureDir.cdUp();
338 #elif defined(Q_OS_MAC)
339 if (textureDir.dirName() == "MacOS") { for(int i=0;i<4;++i){ textureDir.cdUp(); if(textureDir.exists("textures")) break; } }
340 #endif
341 textureDir.cd("textures");
342 QFile f( textureDir.absoluteFilePath(finfo.fileName()));
343
344 QString varname = ( ii == 0 ? "Vertex texture: " : "Fragment texture: ");
345 varname += UniformVar::getStringFromUniformType(v.type) + " " + v.name + "<br>";
346 varname += "Filename: " + finfo.fileName() + (f.exists() ? "" : " [<font color=\"red\">not found</font>]");
347
348 for( int k = 0; k < v.textureGLStates.size(); k++ )
349 {
350 varname += "<br>OpenGL state: " + v.textureGLStates[k].getName() + ": " +
351 parser->convertGlStateToString(v.textureGLStates[k]);
352 }
353
354 QLabel * lblvar = new QLabel(varname);
355 lblvar -> setTextFormat( Qt::RichText );
356 lblvar -> setObjectName( v.name + "00" );
357 layoutTextures->addWidget( lblvar, row++, 0, 1, 2 );
358 shown.append(lblvar);
359
360 QLineEdit * txtChoose = new QLineEdit( textureDir.absoluteFilePath(finfo.fileName()) );
361 txtChoose -> setObjectName( v.name + "11" );
362 layoutTextures->addWidget( txtChoose, row, 0 );
363 shown.append(txtChoose);
364
365 connect(txtChoose, SIGNAL(editingFinished()), signaler, SLOT(map()));
366 signaler->setMapping(txtChoose, v.name + "11");
367
368 QPushButton * btnChoose = new QPushButton( "Browse" );
369 btnChoose -> setObjectName( v.name + "22" );
370 layoutTextures->addWidget( btnChoose, row, 1 );
371 shown.append(btnChoose);
372
373 connect(btnChoose, SIGNAL(clicked()), signaler, SLOT(map()));
374 signaler->setMapping(btnChoose, v.name + "22");
375
376 row++;
377 }
378
379 // OpenGL Status
380 if( pass_selected -> openGLStatesSize() == 0 )
381 {
382 QLabel * lblgl = new QLabel( "No openGL states set" );
383 layoutOpengl->addWidget( lblgl, row, 0 );
384 shown.append(lblgl);
385 }
386 else
387 {
388 for( int i = 0, row = 0; i < pass_selected -> openGLStatesSize(); i++ )
389 {
390 QString str = "OpenGL state: " + pass_selected -> getOpenGLState(i).getName();
391 str += " (" + QString().setNum(pass_selected -> getOpenGLState(i).getState()) + "): " +
392 QString().setNum(pass_selected -> getOpenGLState(i).getValue());
393 QLabel * lblgl = new QLabel(str);
394 layoutOpengl->addWidget( lblgl, row++, 0 );
395 shown.append(lblgl);
396 }
397 }
398 }
399
clearTabs()400 void RmShaderDialog::clearTabs()
401 {
402 for( int i = 0; i < shown.size(); i++ )
403 {
404 shown[i] -> hide();
405 shown[i] -> close();
406 delete shown[i];
407 }
408
409 shown.clear();
410
411 ui.textVertex -> clear();
412 ui.textFragment -> clear();
413 }
414
valuesChanged(const QString & varNameAndIndex)415 void RmShaderDialog::valuesChanged(const QString & varNameAndIndex )
416 {
417 int len = varNameAndIndex.length();
418 int colIdx = QString(varNameAndIndex[ len - 1 ]).toInt();
419 int rowIdx = QString(varNameAndIndex[ len - 2 ]).toInt();
420 QString varname = varNameAndIndex.left(len-2);
421
422 QLabel * lbl = NULL;
423 QLineEdit * txt = NULL;
424 bool isTextureFileEdit = false;
425
426
427 QVariant val;
428 for( int i = 0; val.isNull() && !isTextureFileEdit && i < shown.size(); i++ )
429 {
430 if( shown[i] -> objectName() == varNameAndIndex )
431 {
432 QDoubleSpinBox * dinp = dynamic_cast<QDoubleSpinBox*>(shown[i]);
433 if( dinp ) { val = QVariant(dinp -> value()); }
434 QSpinBox * sinp = dynamic_cast<QSpinBox*>(shown[i]);
435 if( sinp ) { val = QVariant(sinp -> value()); }
436 QCheckBox * cinp = dynamic_cast<QCheckBox*>(shown[i]);
437 if( cinp ) { val = QVariant(cinp -> checkState() == Qt::Checked); }
438 QLineEdit * linp = dynamic_cast<QLineEdit*>(shown[i]);
439 if( linp ) { val = QVariant(linp -> text()); isTextureFileEdit = true; }
440 QPushButton * binp = dynamic_cast<QPushButton*>(shown[i]);
441 if( binp ) {
442 isTextureFileEdit = true;
443
444 // choose the filename with a dialog
445 QFileDialog fd(0,"Choose new texture");
446
447 QDir texturesDir = QDir(qApp->applicationDirPath());
448 #if defined(Q_OS_WIN)
449 if (texturesDir.dirName() == "debug" || texturesDir.dirName() == "release") texturesDir.cdUp();
450 #elif defined(Q_OS_MAC)
451 if (texturesDir.dirName() == "MacOS") { for(int i=0;i<4;++i){ texturesDir.cdUp(); if(texturesDir.exists("textures")) break; } }
452 #endif
453 texturesDir.cd("textures");
454
455 fd.setDirectory(texturesDir);
456 fd.move(500, 100);
457 if (fd.exec())
458 val = fd.selectedFiles().at(0);
459 else
460 return;
461 }
462 }
463 if( !lbl && (lbl = dynamic_cast<QLabel*>(shown[i])) && lbl -> objectName().left(len-2) != varname ) lbl = NULL;
464 if( !txt && (txt = dynamic_cast<QLineEdit*>(shown[i])) && txt -> objectName().left(len-2) != varname ) txt = NULL;
465 }
466
467 if( val.isNull() )
468 {
469 qWarning( "Uniform Variable changed in the dialog, but no valid input found.. fix me! (no change done)");
470 return;
471 }
472
473 // if it's a texture file update the info shown in the dialog
474 if( isTextureFileEdit )
475 {
476 txt -> setText( val.toString() );
477 QString label = lbl -> text();
478 int statusStart = label.indexOf("Filename: ");
479 int statusEnd = label.indexOf(']');
480 QFileInfo finfo(val.toString());
481 if( finfo.exists() ) lbl -> setText( label.mid(0,statusStart) + "Filename: " + finfo.fileName() + " [<font color=\"blue\">ok</font>" + label.mid(statusEnd) );
482 else lbl -> setText( label.mid(0,statusStart) + "Filename: " + finfo.fileName() + " [<font color=\"red\">not found</font>" + label.mid(statusEnd) );
483 }
484
485 holder -> updateUniformVariableValuesFromDialog( pass_selected -> getName(), varname, rowIdx, colIdx, val );
486
487 glarea -> updateGL();
488 }
489
490