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