2020-10-21 17:24:01 +02:00

824 lines
27 KiB
C++

/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005-2008 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include "rfx_dialog.h"
const float RfxDialog::DECTOINT = 10000.0f;
const float RfxDialog::INTTODEC = 0.0001f;
RfxDialog::RfxDialog(RfxShader *s, QAction *a, QGLWidget *parent)
: QDockWidget(parent)
{
shader = s;
mGLWin = parent;
ui.setupUi(this);
setWindowTitle("RenderRfx [" + a->text() + "]");
setWidget(ui.RfxDockContents);
setFeatures(QDockWidget::AllDockWidgetFeatures);
setAllowedAreas(Qt::LeftDockWidgetArea);
setFloating(true);
/* Passes */
QListIterator<RfxGLPass*> pit = s->PassesIterator();
while (pit.hasNext()) {
RfxGLPass *pass = pit.next();
ui.comboPasses->addItem("Pass #" +
QString().setNum(pass->GetPassIndex()) +
" " + pass->GetPassName() + " ");
}
// start from first pass
connect(ui.comboPasses, SIGNAL(currentIndexChanged(int)), this,
SLOT(PassSelected(int)));
selPass = 0;
ui.comboPasses->setCurrentIndex(0);
QFont fixedfont;
fixedfont.setFamily("Courier");
fixedfont.setFixedPitch(true);
fixedfont.setPointSize(10);
ui.textVert->setFont(fixedfont);
ui.textFrag->setFont(fixedfont);
vertHL = new GLSLSynHlighter(ui.textVert->document());
fragHL = new GLSLSynHlighter(ui.textFrag->document());
// fill log once for dialog, since it reports status for all passes
ui.textLog->setPlainText(s->GetCompilationLog().join("\n"));
// uniform tab will contain every uniform from every pass
/* Uniforms */
pit = s->PassesIterator();
while (pit.hasNext()) {
RfxGLPass *pass = pit.next();
QListIterator<RfxUniform*> it = pass->UniformsIterator();
int unifCount = -1; // keep track of uniform index
while (it.hasNext()) {
unifCount++;
RfxUniform *uni = it.next();
if (uni->isTexture())
continue;
/* Verifies if the type of the uniform is special or not */
using namespace std;
const type_info& ti = typeid(*uni);
if(ti != typeid(RfxSpecialUniform))
AddUniformBox(uni, unifCount);
else{
/* If the uniform is a special one, casts it to special uniform */
RfxSpecialUniform* sp = dynamic_cast<RfxSpecialUniform*>(uni);
/* and initializes its value */
sp->initialize();
sp->PassToShader();
}
}
++selPass;
}
selPass = 0;
setupTabs();
}
void RfxDialog::setupTabs()
{
/* Textures */
ui.comboTextures->clear();
ui.comboTextures->setEnabled(true);
disconnect(ui.comboTextures, 0, 0, 0);
QListIterator<RfxUniform*> it = shader->GetPass(selPass)->UniformsIterator();
int unifCount = -1;
while (it.hasNext()) {
unifCount++;
RfxUniform *uni = it.next();
if (!uni->isTexture())
continue;
ui.comboTextures->addItem("[" +
RfxUniform::GetTypeString(uni->GetType()) +
"] " +
((uni->isRenderable())? " (RT) " : "") +
uni->GetName(),
unifCount);
}
if (ui.comboTextures->count() == 0) {
ui.comboTextures->addItem("No textures");
ui.comboTextures->setDisabled(true);
} else {
ui.comboTextures->insertItem(0, "Select...");
ui.comboTextures->setCurrentIndex(0);
connect(ui.comboTextures, SIGNAL(currentIndexChanged(int)), this,
SLOT(TextureSelected(int)));
}
disconnect(ui.btnChangeTexture, 0, 0, 0);
ui.BoxTextureProps->setTitle("");
ui.EditTexFile->clear();
ui.TexStatesTable->clear();
ui.TexStatesTable->setRowCount(0);
ui.TexStatesTable->setColumnCount(2);
ui.TexStatesTable->horizontalHeader()->setStretchLastSection(true);
ui.TexStatesTable->horizontalHeader()->hide();
ui.TexStatesTable->verticalHeader()->hide();
ui.lblPreview->clear();
/* States */
ui.glStatesTable->clear();
ui.glStatesTable->setRowCount(0);
ui.glStatesTable->setColumnCount(2);
ui.glStatesTable->horizontalHeader()->setStretchLastSection(true);
ui.glStatesTable->horizontalHeader()->hide();
ui.glStatesTable->verticalHeader()->hide();
QListIterator<RfxState*> sit = shader->GetPass(selPass)->StatesIterator();
int rowidx = 0;
while (sit.hasNext()) {
RfxState *r = sit.next();
// QTableWidget will take ownership of objects, do not delete them.
QTableWidgetItem *c0 = new QTableWidgetItem(r->GetRenderState());
c0->setFlags(Qt::ItemIsSelectable);
QTableWidgetItem *c1 = new QTableWidgetItem(r->GetRenderValue());
c1->setFlags(Qt::ItemIsSelectable);
ui.glStatesTable->insertRow(rowidx);
ui.glStatesTable->setItem(rowidx, 0, c0);
ui.glStatesTable->setItem(rowidx, 1, c1);
ui.glStatesTable->resizeRowToContents(rowidx);
++rowidx;
}
ui.glStatesTable->resizeColumnToContents(0);
ui.glStatesTable->resizeColumnToContents(1);
/* Vertex/Fragment source view */
ui.textVert->setPlainText(shader->GetPass(selPass)->GetVertexSource());
ui.textFrag->setPlainText(shader->GetPass(selPass)->GetFragmentSource());
}
RfxDialog::~RfxDialog()
{
delete vertHL;
delete fragHL;
CleanTab(ALL_TABS);
}
void RfxDialog::PassSelected(int idx)
{
selPass = idx;
CleanTab(ALL_TABS);
setupTabs();
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::AddUniformBox(RfxUniform *uni, int uniIndex)
{
assert(uni);
QLabel *lblUni = new QLabel();
QString lblText;
lblText.append(QString("(Pass #%1) ").arg(selPass));
lblText.append(uni->GetName());
if (!uni->GetSemantic().isNull()) {
lblText.append("<span style=\"color:darkgreen;\"><br/> [P: ");
lblText.append(uni->GetSemantic());
lblText.append("]</span>");
}
lblUni->setText(lblText);
QGridLayout *gridUni = new QGridLayout();
switch (uni->GetType()) {
case RfxUniform::INT:
case RfxUniform::FLOAT:
case RfxUniform::BOOL:
DrawIFace(gridUni, uni, uniIndex, 1, 1);
break;
case RfxUniform::VEC2:
case RfxUniform::IVEC2:
case RfxUniform::BVEC2:
DrawIFace(gridUni, uni, uniIndex, 1, 2);
break;
case RfxUniform::VEC3:
case RfxUniform::IVEC3:
case RfxUniform::BVEC3:
DrawIFace(gridUni, uni, uniIndex, 1, 3);
break;
case RfxUniform::VEC4:
if(uni->isRmColorVariable()){
DrawIFace(gridUni, uni, uniIndex, 1, 1);
break;
}
case RfxUniform::IVEC4:
case RfxUniform::BVEC4:
DrawIFace(gridUni, uni, uniIndex, 1, 4);
break;
case RfxUniform::MAT2:
DrawIFace(gridUni, uni, uniIndex, 2, 2);
break;
case RfxUniform::MAT3:
DrawIFace(gridUni, uni, uniIndex, 3, 3);
break;
case RfxUniform::MAT4:
DrawIFace(gridUni, uni, uniIndex, 4, 4);
break;
default:
return;
}
QHBoxLayout *boxContent = new QHBoxLayout();
boxContent->addWidget(lblUni);
boxContent->addLayout(gridUni);
((QVBoxLayout*)ui.scrollUniformsContents->layout())->addLayout(boxContent);
}
void RfxDialog::DrawIFace(QGridLayout *parent, RfxUniform *u, int uidx, int rows, int columns)
{
enum controlType { INT_CTRL, FLOAT_CTRL, BOOL_CTRL, COLOR_CTRL };
enum ColorComponentsType { R, G, B, A };
float *val = u->GetValue();
controlType ctrl;
ColorComponentsType rgba = R;
QWidget **controls = new QWidget*[rows * columns];
QGridLayout *uniLayout = parent;
QHBoxLayout *sliderEditLayout = NULL;
bool multipleControls = false;
QSignalMapper *valMapper = new QSignalMapper(this);
switch (u->GetType()) {
case RfxUniform::INT:
case RfxUniform::IVEC2:
case RfxUniform::IVEC3:
case RfxUniform::IVEC4:
ctrl = INT_CTRL;
break;
case RfxUniform::FLOAT:
case RfxUniform::VEC2:
case RfxUniform::VEC3:
case RfxUniform::VEC4:
if(u->isRmColorVariable()){
ctrl = COLOR_CTRL;
break;
}
case RfxUniform::MAT2:
case RfxUniform::MAT3:
case RfxUniform::MAT4:
ctrl = FLOAT_CTRL;
break;
case RfxUniform::BOOL:
case RfxUniform::BVEC2:
case RfxUniform::BVEC3:
case RfxUniform::BVEC4:
ctrl = BOOL_CTRL;
break;
default:
return;
}
if(ctrl == COLOR_CTRL)
{
RfxColorBox* mycolorBox = new RfxColorBox(100, 25, QColor(val[0] * 255, val[1] * 255, val[2] * 255, val[3] * 255));
uniLayout->addWidget(mycolorBox, 0,0);
connect(mycolorBox, SIGNAL(colorChanged()), valMapper, SLOT(map()));
connect(mycolorBox,SIGNAL(colorChanged()),mGLWin, SLOT(update()));
valMapper->setMapping(mycolorBox,
QString().setNum(uidx) + '-' +
QString().setNum(1) + '-' +
QString().setNum(selPass));
}
else{
// controls in a grid layout
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j) {
int arrayIdx = j + (i * rows);
switch (ctrl) {
case INT_CTRL:
controls[arrayIdx] = new QSpinBox(this);
((QSpinBox*)controls[arrayIdx])->setRange(-99, 99);
((QSpinBox*)controls[arrayIdx])->setValue((int)val[arrayIdx]);
connect(controls[arrayIdx], SIGNAL(valueChanged(int)),
valMapper, SLOT(map()));
connect(controls[arrayIdx], SIGNAL(valueChanged(int)), this,
SLOT(extendRange(int)));
break;
case FLOAT_CTRL:
if (u->HasMinMax()) {
controls[arrayIdx] = new QSlider(this);
((QSlider*)controls[arrayIdx])->setTickPosition(QSlider::NoTicks);
((QSlider*)controls[arrayIdx])->setOrientation(Qt::Horizontal);
// since qslider only deals with integers, do a little conversion
// of values
// keep as much as 5 decimal values, others will be lost in conversion
int valAsInt = (int)(val[arrayIdx] * DECTOINT);
int tickAsInt = (int)(((u->GetMaxRange() - u->GetMinRange()) * 0.01) * DECTOINT);
((QSlider*)controls[arrayIdx])->setTickInterval(tickAsInt);
((QSlider*)controls[arrayIdx])->setRange((int)(u->GetMinRange() * DECTOINT),
(int)(u->GetMaxRange() * DECTOINT));
((QSlider*)controls[arrayIdx])->setValue(valAsInt);
((QSlider*)controls[arrayIdx])->setToolTip(QString().setNum(val[arrayIdx]));
connect(controls[arrayIdx], SIGNAL(valueChanged(int)), valMapper, SLOT(map()));
// as per request, if value is a single float, also show a qlineedit to allow
// more accurate settings.
if (u->GetType() == RfxUniform::FLOAT) {
QLineEdit *slideValue = new QLineEdit();
slideValue->setAlignment(Qt::AlignRight);
slideValue->setText(QString().setNum(val[arrayIdx]));
QSignalMapper *yaMapper = new QSignalMapper();
connect(controls[arrayIdx], SIGNAL(valueChanged(int)), yaMapper, SLOT(map()));
connect(slideValue, SIGNAL(editingFinished()), yaMapper, SLOT(map()));
yaMapper->setMapping(controls[arrayIdx], slideValue);
yaMapper->setMapping(slideValue, controls[arrayIdx]);
connect(yaMapper, SIGNAL(mapped(QWidget*)), this, SLOT(mapSliderLineEdit(QWidget*)));
if (!u->GetSemantic().isNull())
slideValue->setDisabled(true);
multipleControls = true;
sliderEditLayout = new QHBoxLayout();
sliderEditLayout->addWidget(controls[arrayIdx]);
sliderEditLayout->addWidget(slideValue);
}
} else {
controls[arrayIdx] = new QDoubleSpinBox(this);
((QDoubleSpinBox*)controls[arrayIdx])->setRange(-99.0, 99.0);
((QDoubleSpinBox*)controls[arrayIdx])->setValue(val[arrayIdx]);
((QDoubleSpinBox*)controls[arrayIdx])->setDecimals(4);
((QDoubleSpinBox*)controls[arrayIdx])->setSingleStep(0.01);
connect(controls[arrayIdx], SIGNAL(valueChanged(double)),
valMapper, SLOT(map()));
connect(controls[arrayIdx], SIGNAL(valueChanged(double)), this,
SLOT(extendRange(double)));
}
break;
case BOOL_CTRL:
controls[arrayIdx] = new QComboBox(this);
((QComboBox*)controls[arrayIdx])->addItem("FALSE");
((QComboBox*)controls[arrayIdx])->addItem("TRUE");
if (!val[arrayIdx])
((QComboBox*)controls[arrayIdx])->setCurrentIndex(0);
connect(controls[arrayIdx], SIGNAL(currentIndexChanged(int)),
valMapper, SLOT(map()));
break;
}
valMapper->setMapping(controls[arrayIdx],
QString().setNum(uidx) + '-' +
QString().setNum(arrayIdx) + '-' +
QString().setNum(selPass));
if (!u->GetSemantic().isNull())
controls[arrayIdx]->setDisabled(true);
if (multipleControls)
uniLayout->addLayout(sliderEditLayout, i, j);
else
uniLayout->addWidget(controls[arrayIdx], i, j);
multipleControls = false;
}
}
}
connect(valMapper, SIGNAL(mapped(const QString&)), this,
SLOT(ChangeValue(const QString&)));
}
void RfxDialog::mapSliderLineEdit(QWidget *w)
{
QObject *theSender = ((QSignalMapper*)QObject::sender())->mapping(w);
QSlider *qslide = dynamic_cast<QSlider*>(theSender);
if (qslide != NULL) {
((QLineEdit*)w)->setText(QString().setNum((float)(qslide->value()) * INTTODEC));
} else {
// we assume it's a QLineEdit (avoid a costly and useless dynamic_cast)
// let's do some more checks since qlineedit input is not limited in range
bool res = false;
float val = ((QLineEdit*)theSender)->text().toFloat(&res);
qslide = ((QSlider*)w);
// we only accept float values
if (!res)
return;
// if value is beyond min or max, just set min/max
if (val > qslide->maximum() * INTTODEC)
qslide->setValue(qslide->maximum());
else if (val < qslide->minimum() * INTTODEC)
qslide->setValue(qslide->minimum());
else
qslide->setValue((int)(val * DECTOINT));
}
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::CleanTab(int tabIdx)
{
// deletes all widgets from specified tab or, if tabIdx == -1
// removes all widgets from all tabs
// (this applies to *dynamically created* widgets only)
if (tabIdx == ALL_TABS) {
QMapIterator<int, QWidget*> it(widgetsByTab);
while (it.hasNext()) {
QWidget *toDelete = it.next().value();
toDelete->close();
delete toDelete;
}
widgetsByTab.clear();
} else {
QList<QWidget*> toDelete = widgetsByTab.values(tabIdx);
for (int i = 0; i < toDelete.size(); ++i) {
toDelete.at(i)->close();
delete toDelete.at(i);
}
widgetsByTab.remove(tabIdx);
}
}
void RfxDialog::extendRange(double newVal)
{
QDoubleSpinBox *sender = (QDoubleSpinBox*)QObject::sender();
if (newVal == sender->minimum() || newVal == sender->maximum()) {
if (newVal == sender->minimum())
sender->setMinimum(newVal - 50);
else
sender->setMaximum(newVal + 50);
}
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::extendRange(int newVal)
{
QSpinBox *sender = (QSpinBox*)QObject::sender();
if (newVal == sender->minimum() || newVal == sender->maximum()) {
if (newVal == sender->minimum())
sender->setMinimum(newVal - 50);
else
sender->setMaximum(newVal + 50);
}
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::ChangeTexture(int unifIdx)
{
int uniIndex = ui.comboTextures->itemData(unifIdx).toInt();
RfxUniform *uni = shader->GetPass(selPass)->getUniform(uniIndex);
assert(uni);
QString fname = QFileDialog::getOpenFileName(this,
tr("Choose Texture"),
uni->GetTextureFName());
if (!fname.isEmpty()) {
uni->SetValue(QDir::fromNativeSeparators(fname));
uni->LoadTexture();
uni->PassToShader();
mGLWin->updateGL();
// generate a currentIndexChanged event
ui.comboTextures->setCurrentIndex(0);
ui.comboTextures->setCurrentIndex(unifIdx);
}
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::ChangeValue(const QString& val)
{
QStringList unif = val.split('-');
RfxUniform *uni = shader->GetPass(unif[2].toInt())->getUniform(unif[0].toInt());
float *oldVal = uni->GetValue();
float newVal = 0.0f;
QObject *sender = ((QSignalMapper*)QObject::sender())->mapping(val);
assert(sender);
QComboBox *cbox = dynamic_cast<QComboBox*>(sender);
if (cbox != NULL) {
newVal = cbox->currentIndex();
} else {
QSpinBox *sbox = dynamic_cast<QSpinBox*>(sender);
if (sbox != NULL) {
newVal = sbox->value();
} else {
QDoubleSpinBox *dsbox = dynamic_cast<QDoubleSpinBox*>(sender);
if (dsbox != NULL) {
newVal = dsbox->value();
} else {
QSlider *qslide = dynamic_cast<QSlider*>(sender);
if (qslide != NULL) {
newVal = qslide->value() * INTTODEC;
qslide->setToolTip(QString().setNum(newVal));
} else {
RfxColorBox *cBox = dynamic_cast<RfxColorBox*>(sender);
if (cBox != NULL) {
vcg::Color4f newVal = cBox->getColorf();
oldVal[0] = newVal.X();
oldVal[1] = newVal.Y();
oldVal[2] = newVal.Z();
oldVal[3] = newVal.W();
uni->PassToShader();
mGLWin->updateGL();
return;
} else {
return;
}
}
}
}
}
oldVal[unif[1].toInt()] = newVal;
uni->PassToShader();
if (mGLWin != NULL)
mGLWin->update();
}
void RfxDialog::TextureSelected(int idx)
{
disconnect(ui.btnChangeTexture, 0, 0, 0);
if (idx <= 0)
return;
int uniIndex = ui.comboTextures->itemData(idx).toInt();
RfxUniform *uni = shader->GetPass(selPass)->getUniform(uniIndex);
assert(uni);
// connect Change Texture button
QSignalMapper *sigMap = new QSignalMapper(this);
connect(ui.btnChangeTexture, SIGNAL(clicked()), sigMap, SLOT(map()));
sigMap->setMapping(ui.btnChangeTexture, idx);
connect(sigMap, SIGNAL(mapped(int)), this, SLOT(ChangeTexture(int)));
ui.BoxTextureProps->setTitle(uni->GetName());
// clean and initialize table
ui.TexStatesTable->clear();
ui.TexStatesTable->setRowCount(0);
ui.TexStatesTable->setColumnCount(2);
ui.TexStatesTable->horizontalHeader()->setStretchLastSection(true);
ui.TexStatesTable->horizontalHeader()->hide();
ui.TexStatesTable->verticalHeader()->hide();
// delete any existing preview
ui.lblPreview->clear();
CleanTab(TEXTURE_TAB);
if (uni->isRenderable())
ui.EditTexFile->clear();
else
ui.EditTexFile->setText(uni->GetTextureFName());
QLabel *fname = new QLabel();
ui.infoTexLayout->addWidget(fname);
widgetsByTab.insert(TEXTURE_TAB, fname);
if (!uni->isTextureFound() || !uni->isTextureLoaded()) {
if (!uni->isTextureFound())
fname->setText("<span style=\"color:darkred;\">Texture file not found</span>");
else
fname->setText("<span style=\"color:darkred;\">Texture binding failed</span>");
} else {
fname->setText("<span style=\"color:darkgreen;\">Texture loaded</span>");
ImageInfo ii;
bool loaded;
if (uni->isRenderable()) {
ii.preview = uni->GetRTTexture();
ii.texType = "Render Target";
ii.width = (ii.preview.isNull())? 0 : ii.preview.width();
ii.height = (ii.preview.isNull())? 0 : ii.preview.height();
ii.depth = 1;
ii.format = "";
} else {
ii = RfxTextureLoader::LoadAsQImage(uni->GetTextureFName());
}
loaded = !ii.preview.isNull();
if (loaded) {
QLabel *tSize = new QLabel();
tSize->setText("Dimensions: " +
QString().setNum(ii.width) + " x " +
QString().setNum(ii.height) +
((ii.depth > 1)? " x " + QString().setNum(ii.depth) : ""));
ui.infoTexLayout->addWidget(tSize);
widgetsByTab.insert(TEXTURE_TAB, tSize);
QLabel *tType = new QLabel();
tType->setText("Type: " + ii.texType);
ui.infoTexLayout->addWidget(tType);
widgetsByTab.insert(TEXTURE_TAB, tType);
QLabel *tFormat = new QLabel();
tFormat->setText("Format: " + ii.format);
ui.infoTexLayout->addWidget(tFormat);
widgetsByTab.insert(TEXTURE_TAB, tFormat);
// try to get a preview
QPixmap prvw = QPixmap::fromImage(ii.preview);
if (!prvw.isNull()) {
QSize scaledSize(120, 120);
if (ii.texType == "Cubemap Texture")
scaledSize.setWidth(200);
ui.lblPreview->setPixmap(prvw.scaled(scaledSize,
Qt::KeepAspectRatio));
}
}
QLabel *tunit = new QLabel();
tunit->setText("Texture Unit: " + QString().setNum(uni->GetTU()));
ui.infoTexLayout->addWidget(tunit);
widgetsByTab.insert(TEXTURE_TAB, tunit);
}
QListIterator<RfxState*> it = uni->StatesIterator();
int rowidx = 0;
while (it.hasNext()) {
RfxState *r = it.next();
// QTableWidget will take ownership of objects, do not delete them.
QTableWidgetItem *c0 = new QTableWidgetItem(r->GetTextureState());
c0->setFlags(Qt::ItemIsSelectable);
QTableWidgetItem *c1 = new QTableWidgetItem(r->GetTextureValue());
c1->setFlags(Qt::ItemIsSelectable);
ui.TexStatesTable->insertRow(rowidx);
ui.TexStatesTable->setItem(rowidx, 0, c0);
ui.TexStatesTable->setItem(rowidx, 1, c1);
ui.TexStatesTable->resizeRowToContents(rowidx);
++rowidx;
}
ui.TexStatesTable->resizeColumnToContents(0);
ui.TexStatesTable->resizeColumnToContents(1);
if (mGLWin != NULL)
mGLWin->update();
}
/*********************************
* GLSL Syntax Highlighter Class *
*********************************/
GLSLSynHlighter::GLSLSynHlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
HighlightingRule rule;
kwordsFormat.setForeground(Qt::blue);
QStringList keywords;
keywords << "uniform" << "int" << "float" << "bool" << "inout"
<< "vec2" << "vec3" << "vec4" << "ivec2" << "out"
<< "ivec3" << "ivec4" << "bvec2" << "bvec3" << "in"
<< "bvec4" << "mat2" << "mat3" << "mat4" << "attribute"
<< "sampler1D" << "sampler2D" << "sampler3D"
<< "samplerCube" << "sampler1DShadow" << "void"
<< "sampler2DShadow" << "varying" << "const";
foreach (QString pattern, keywords) {
rule.pattern = QRegExp("\\b" + pattern + "\\b");
rule.format = kwordsFormat;
highlightingRules.append(rule);
}
builtinsFormat.setForeground(Qt::magenta);
QStringList builtins;
builtins << "gl_Position" << "gl_ClipSize" << "gl_ClipVertex"
<< "gl_Vertex" << "gl_Normal" << "gl_Color" << "gl_FragColor"
<< "gl_SecondaryColor" << "gl_FogCoord" << "gl_MultiTexCoord[0-7]"
<< "gl_FrontColor" << "gl_BackColor" << "gl_FrontSecondaryColor"
<< "gl_BackSecondaryColor" << "gl_TexCoord" << "gl_FogFragCoord"
<< "gl_FragData" << "gl_FrontFacing" << "gl_FragCoord"
<< "gl_FragDepth" << "gl_ModelViewMatrix" << "gl_ProjectionMatrix"
<< "gl_ModelViewProjectionMatrix" << "gl_ModelViewMatrixInverse"
<< "gl_ModelViewProjectionMatrixInverse" << "gl_NormalMatrix"
<< "gl_NormalScale" << "gl_DepthRange" << "gl_Point"
<< "gl_ModelViewMatrixInverseTranspose" << "gl_LightSource"
<< "gl_ModelViewMatrixTranspose" << "gl_ProjectionMatrixTranspose"
<< "gl_ModelViewProjectionMatrixInverseTranspose" << "gl_Fog"
<< "gl_ClipPlane" << "gl_(Eye|Object)Plane[STRQ]"
<< "gl_(Front|Back)Material" << "gl_(Front|Back)LightProduct";
foreach (QString pattern, builtins) {
rule.pattern = QRegExp("\\b" + pattern + "\\b");
rule.format = builtinsFormat;
highlightingRules.append(rule);
}
functionFormat.setForeground(QColor::fromRgb(255, 0, 141));
QStringList functions;
functions << "sin" << "cos" << "tan" << "asin" << "acos" << "atan"
<< "radians" << "degrees" << "pow" << "exp" << "log"
<< "expr2" << "log2" << "sqrt" << "inversesqrt" << "abs"
<< "ceil" << "clamp" << "floor" << "fract" << "min" << "mix"
<< "max" << "mod" << "sign" << "smoothstep" << "step"
<< "ftransform" << "cross" << "distance" << "dot"
<< "faceforward" << "length" << "normalize" << "reflect"
<< "dFdx" << "dFdy" << "fwidth" << "matrixCompMult" << "all"
<< "any" << "equal" << "greaterThan" << "lessThan" << "notEqual"
<< "texture1DProj" << "texture2DProj" << "texture3DProj"
<< "textureCube" << "noise4" << "texture3D" << "not" << "noise3"
<< "texture1D" << "texture2D" << "noise1" << "noise2";
foreach (QString pattern, functions) {
rule.pattern = QRegExp("\\b" + pattern + "+(?=\\()");
rule.format = functionFormat;
highlightingRules.append(rule);
}
singleLineCommentFormat.setForeground(Qt::darkGreen);
rule.pattern = QRegExp("//[^\n]*");
rule.format = singleLineCommentFormat;
highlightingRules.append(rule);
multiLineCommentFormat = singleLineCommentFormat;
commentStartExpression = QRegExp("/\\*");
commentEndExpression = QRegExp("\\*/");
}
void GLSLSynHlighter::highlightBlock(const QString &text)
{
foreach (HighlightingRule rule, highlightingRules) {
QRegExp expression(rule.pattern);
int index = text.indexOf(expression);
while (index >= 0) {
int length = expression.matchedLength();
setFormat(index, length, rule.format);
index = text.indexOf(expression, index + length);
}
}
/* multiline comment management */
setCurrentBlockState(0);
int startIndex = 0;
if (previousBlockState() != 1)
startIndex = text.indexOf(commentStartExpression);
while (startIndex >= 0) {
int endIndex = text.indexOf(commentEndExpression, startIndex);
int commentLength;
if (endIndex == -1) {
setCurrentBlockState(1);
commentLength = text.length() - startIndex;
} else {
commentLength = endIndex - startIndex +
commentEndExpression.matchedLength();
}
setFormat(startIndex, commentLength, multiLineCommentFormat);
startIndex = text.indexOf(commentStartExpression,
startIndex + commentLength);
}
}