mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-15 00:54:38 +00:00
512 lines
16 KiB
C++
Executable File
512 lines
16 KiB
C++
Executable File
/****************************************************************************
|
|
* 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_parser.h"
|
|
|
|
RfxParser::RfxParser(const QString &rfxFile)
|
|
{
|
|
rmShader = new QFile(rfxFile);
|
|
rmShader->open(QIODevice::ReadOnly | QIODevice::Text);
|
|
|
|
rfxShader = new RfxShader();
|
|
}
|
|
|
|
RfxParser::~RfxParser()
|
|
{
|
|
rmShader->close();
|
|
delete rmShader;
|
|
}
|
|
|
|
/*
|
|
* Each .rfx file (should be valid XML) contains a DTD for its own validation,
|
|
* followed by a list of RmRegistryBranch.
|
|
* This header is documented in default RM installation directory:
|
|
* [Data/RmDTDHeader.rfx] contains DTD
|
|
* [Data/rm_registry.rfx] contains the list of registers and their value ranges
|
|
*
|
|
* Rfx file may contain RmDirectXEffect, RmOpenGLEffect and RmOpenGLESEffect.
|
|
* We only care about the second type.
|
|
*
|
|
* <!DOCTYPE RENDERMONKEY[
|
|
* ... (DTD here)
|
|
* ]>
|
|
* <RENDER_MONKEY>
|
|
* <RmRegistryBranch>
|
|
* ...
|
|
* </RmRegistryBranch>
|
|
* <RmOpenGLEffect ... >
|
|
* ...
|
|
* </RRmOpenGLEffect>
|
|
* </RENDER_MONKEY>
|
|
*
|
|
* TODO: use DTD in rfx header to validate contents
|
|
*/
|
|
|
|
/*
|
|
Verifies if the document is valid.
|
|
@return true if the document is valid, false otherwise.
|
|
*/
|
|
bool RfxParser::isValidDoc()
|
|
{
|
|
if (!document.setContent(rmShader))
|
|
return false;
|
|
|
|
root = document.documentElement();
|
|
QDomElement OGLEffect =
|
|
root.elementsByTagName("RmOpenGLEffect").at(0).toElement();
|
|
|
|
// no <RmOpenGLEffect> found, parsing failed
|
|
if (OGLEffect.isNull())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool RfxParser::Parse(MeshDocument &md)
|
|
{
|
|
document.setContent(rmShader);
|
|
root = document.documentElement();
|
|
QDomElement OGLEffect =
|
|
root.elementsByTagName("RmOpenGLEffect").at(0).toElement();
|
|
|
|
// before looping between passes, check for renderable textures
|
|
QDomNodeList rtex = root.elementsByTagName("RmRenderableTexture");
|
|
for (int i = 0; i < rtex.size(); ++i) {
|
|
QDomElement rtEl = rtex.at(i).toElement();
|
|
|
|
RfxRenderTarget *rt = new RfxRenderTarget(rtEl.attribute("NAME"));
|
|
rt->SetSize(rtEl.attribute("WIDTH").toInt(),
|
|
rtEl.attribute("HEIGHT").toInt());
|
|
|
|
rt->UseViewPortDim(rtEl.attribute("USE_VIEWPORT_DIMENSIONS") == "TRUE");
|
|
rt->GenMipmaps(rtEl.attribute("GENERATE_MIPMAPS") == "TRUE");
|
|
|
|
rfxShader->AddRT(rt);
|
|
}
|
|
|
|
QDomNodeList list = OGLEffect.elementsByTagName("RmGLPass");
|
|
for (int i = 0; i < list.size(); ++i) {
|
|
QDomElement glpass = list.at(i).toElement();
|
|
|
|
if (glpass.attribute("ENABLED") == "FALSE")
|
|
continue;
|
|
|
|
RfxGLPass *theGLPass = new RfxGLPass();
|
|
theGLPass->SetPassName(glpass.attribute("NAME"));
|
|
theGLPass->SetPassIndex(glpass.attribute("PASS_INDEX").toInt());
|
|
|
|
// OpenGL states if any
|
|
QDomElement state;
|
|
state = glpass.firstChildElement("RmRenderStateBlock");
|
|
if (!state.isNull()) {
|
|
QDomNodeList statelist = state.elementsByTagName("RmState");
|
|
QList<RfxState*> rslist = ParseGLStates(statelist,
|
|
RfxState::RFX_RENDERSTATE);
|
|
foreach (RfxState *rs, rslist)
|
|
theGLPass->AddGLState(rs);
|
|
}
|
|
|
|
// RenderTarget if this pass renders to texture
|
|
QDomElement eRT = glpass.firstChildElement("RmRenderTarget");
|
|
if (!eRT.isNull()) {
|
|
int idx = rfxShader->FindRT(eRT.attribute("NAME"));
|
|
if (idx != -1) {
|
|
RfxRenderTarget *rt = rfxShader->GetRT(idx);
|
|
|
|
// we need pass number
|
|
int pass = theGLPass->GetPassIndex();
|
|
|
|
// depth clear value
|
|
float depthClear = -1.0f;
|
|
if (eRT.attribute("DEPTH_CLEAR") == "TRUE")
|
|
depthClear = eRT.attribute("DEPTH_CLEAR_VALUE").toFloat();
|
|
|
|
// and color clear
|
|
float *colorClear = NULL;
|
|
if (eRT.attribute("COLOR_CLEAR") == "TRUE") {
|
|
long pColor = eRT.attribute("CLEAR_COLOR_VALUE").toLong();
|
|
colorClear = RfxState::DecodeColor(pColor);
|
|
}
|
|
|
|
rt->SetClear(pass, depthClear, colorClear);
|
|
theGLPass->SetRenderToTexture(true);
|
|
theGLPass->LinkRenderTarget(rt);
|
|
}
|
|
}
|
|
|
|
// shader sources
|
|
QDomNodeList sources = glpass.elementsByTagName("RmGLShader");
|
|
for (int j = 0; j < sources.size(); ++j) {
|
|
QDomElement source = sources.at(j).toElement();
|
|
bool isPixel = (source.attribute("PIXEL_SHADER") == "TRUE");
|
|
|
|
theGLPass->SetShaderSource(source.text(), isPixel);
|
|
|
|
// also check for uniforms declaration, will save some time later
|
|
ParseUniforms(source.text());
|
|
|
|
ParseAttributes(source.text(), theGLPass);
|
|
}
|
|
|
|
// shader uniforms
|
|
QDomNodeList constlist = glpass.elementsByTagName("RmShaderConstant");
|
|
for (int j = 0; j < constlist.size(); ++j) {
|
|
QString varName = constlist.at(j).toElement().attribute("NAME");
|
|
|
|
// check that this uniform is used in shaders, else ignore it
|
|
if (!uniformType.contains(varName))
|
|
continue;
|
|
|
|
RfxUniform *unif;
|
|
if(RfxSpecialUniform::getSpecialType(varName) == RfxSpecialUniform::NONE){
|
|
unif = new RfxUniform(varName, uniformType[varName]);
|
|
float *parsedValue = ValueFromRfx(varName, unif);
|
|
unif->SetValue(parsedValue);
|
|
|
|
if (parsedValue[16] != 0.0 || parsedValue[17] != 0.0)
|
|
unif->SetValRange(parsedValue[16], parsedValue[17]);
|
|
|
|
delete parsedValue;
|
|
}
|
|
else
|
|
unif = new RfxSpecialUniform(varName, uniformType[varName], &md);
|
|
|
|
|
|
QString sem = GetSemantic(varName, unif->GetType());
|
|
if (!sem.isNull()) {
|
|
if (rfxShader->AddSemanticUniform(unif, sem))
|
|
unif->SetSemantic(sem);
|
|
}
|
|
|
|
theGLPass->AddUniform(unif);
|
|
}
|
|
|
|
// shader uniforms - textures and states
|
|
GLint tu = 0;
|
|
QDomNodeList samplist = glpass.elementsByTagName("RmSampler");
|
|
for (int j = 0; j < samplist.size(); ++j) {
|
|
QString TOString = samplist.at(j).toElement().attribute("NAME");
|
|
|
|
QDomNodeList TOList =
|
|
glpass.elementsByTagName("RmTextureObject");
|
|
for (int k = 0; k < TOList.size(); k++) {
|
|
QDomElement to = TOList.at(k).toElement();
|
|
if (to.attribute("NAME") == TOString) {
|
|
QDomElement tr =
|
|
to.firstChildElement("RmTextureReference");
|
|
QString texName = tr.attribute("NAME");
|
|
|
|
// check that this uniform is used in shaders
|
|
if (!uniformType.contains(TOString))
|
|
break;
|
|
|
|
RfxUniform *unif = new RfxUniform(TOString,
|
|
uniformType[TOString]);
|
|
|
|
// set texture unit to use
|
|
unif->SetTU(tu++);
|
|
|
|
// texture maybe from a file or a renderable one
|
|
int rtIdx = -1;
|
|
QString txPath = TextureFromRfx(texName, unif->GetType());
|
|
if (txPath.startsWith("RT:")) {
|
|
rtIdx = txPath.split(":").at(1).toInt();
|
|
unif->SetValue(rfxShader->GetRT(rtIdx));
|
|
} else {
|
|
unif->SetValue(txPath);
|
|
}
|
|
|
|
// set any sampler state
|
|
QDomNodeList statelist = to.elementsByTagName("RmState");
|
|
QList<RfxState*> rslist =
|
|
ParseGLStates(statelist, RfxState::RFX_SAMPLERSTATE);
|
|
|
|
// if texture is renderable add states directly to RT class
|
|
// since it's the one that will glGen the texture
|
|
if (unif->isRenderable()) {
|
|
RfxRenderTarget *rt = rfxShader->GetRT(rtIdx);
|
|
int pass = theGLPass->GetPassIndex();
|
|
|
|
foreach (RfxState *rs, rslist)
|
|
rt->AddGLState(pass, rs);
|
|
} else {
|
|
foreach (RfxState *rs, rslist)
|
|
unif->AddGLState(rs);
|
|
}
|
|
|
|
// add uniform to pass
|
|
theGLPass->AddUniform(unif);
|
|
}
|
|
}
|
|
}
|
|
// clear uniform list for next pass
|
|
uniformType.clear();
|
|
|
|
// finally add to the pass list
|
|
rfxShader->AddGLPass(theGLPass);
|
|
}
|
|
|
|
rfxShader->SortPasses();
|
|
return true;
|
|
}
|
|
|
|
// static member initialization
|
|
// this is a lookup table for mapping between uniform types
|
|
// (ie "vec2") and their counterparts in rfx file (ie "RmVectorVariable")
|
|
const char *RfxParser::UniformToRfx[] = {
|
|
"RmIntegerVariable", "RmFloatVariable", "RmBooleanVariable",
|
|
"RmVectorVariable", "RmVectorVariable", "RmVectorVariable",
|
|
"RmVectorVariable", "RmVectorVariable", "RmVectorVariable",
|
|
"RmVectorVariable", "RmVectorVariable", "RmVectorVariable",
|
|
"RmMatrixVariable", "RmMatrixVariable", "RmMatrixVariable",
|
|
"?", "Rm2DTextureVariable", "Rm3DTextureVariable", "RmCubemapVariable",
|
|
"?", "?"
|
|
};
|
|
|
|
// lookup table for mapping between uniform types and the number
|
|
// of their elements (ie: mat3 => 9 elements)
|
|
const short RfxParser::UniformToElements[] = {
|
|
1, 1, 1,
|
|
2, 3, 4,
|
|
2, 3, 4,
|
|
2, 3, 4,
|
|
4, 9, 16,
|
|
-1, -1, -1, -1,
|
|
-1, -1
|
|
};
|
|
|
|
QString RfxParser::GetSemantic(const QString& VarName, RfxUniform::UniformType VarType)
|
|
{
|
|
QString elementName = UniformToRfx[VarType];
|
|
|
|
// first thing first find the DOM Node of declared var
|
|
QDomElement varNode;
|
|
QDomNodeList candidates = root.elementsByTagName(elementName);
|
|
|
|
for (int i = 0; i < candidates.size(); ++i)
|
|
if (candidates.at(i).toElement().attribute("NAME") == VarName)
|
|
varNode = candidates.at(i).toElement();
|
|
|
|
// if we're looking for a vec4, it could be in a RmColorVariable,
|
|
// so try a second search if the first was unsuccessful
|
|
if (VarType == RfxUniform::VEC4 && varNode.isNull()) {
|
|
candidates = root.elementsByTagName("RmColorVariable");
|
|
|
|
for (int i = 0; i < candidates.size(); ++i)
|
|
if (candidates.at(i).toElement().attribute("NAME") == VarName)
|
|
varNode = candidates.at(i).toElement();
|
|
}
|
|
|
|
if (!varNode.isNull())
|
|
if (varNode.attribute("VARIABLE_SEMANTIC") != "")
|
|
return varNode.attribute("VARIABLE_SEMANTIC");
|
|
|
|
return QString();
|
|
}
|
|
|
|
float* RfxParser::ValueFromRfx(const QString& VarName, RfxUniform* unif)
|
|
{
|
|
// read a bool, int or float vector (max 4 elements)
|
|
// or a float matrix (max 16 elements).
|
|
// values 17 and 18 are reserved for min and max values (if present)
|
|
float *result = new float[18];
|
|
|
|
// initialization (unnecessary?)
|
|
for (int i = 0; i < 18; ++i)
|
|
result[i] = 0.0f;
|
|
|
|
RfxUniform::UniformType VarType = unif->GetType();
|
|
// use lookup table to init data
|
|
int elementNum = UniformToElements[VarType];
|
|
QString elementName = UniformToRfx[VarType];
|
|
|
|
// first thing first find the DOM Node of declared var
|
|
QDomElement varNode;
|
|
QDomNodeList candidates = root.elementsByTagName(elementName);
|
|
|
|
for (int i = 0; i < candidates.size(); ++i)
|
|
if (candidates.at(i).toElement().attribute("NAME") == VarName)
|
|
varNode = candidates.at(i).toElement();
|
|
|
|
// if we're looking for a vec4, it could be in a RmColorVariable,
|
|
// so try a second search if the first was unsuccessful
|
|
if (VarType == RfxUniform::VEC4 && varNode.isNull()) {
|
|
candidates = root.elementsByTagName("RmColorVariable");
|
|
|
|
for (int i = 0; i < candidates.size(); ++i)
|
|
if (candidates.at(i).toElement().attribute("NAME") == VarName) {
|
|
varNode = candidates.at(i).toElement();
|
|
unif->setIsRmColorVariable(true);
|
|
result[16] = 0.0;
|
|
result[17] = 1.0;
|
|
}
|
|
}
|
|
|
|
if (!varNode.isNull()) {
|
|
// retrieve values stored in XML
|
|
for (int i = 0; i < elementNum; ++i) {
|
|
QString attrValue = "VALUE";
|
|
if (elementNum > 1)
|
|
attrValue.append("_").append(QString().setNum(i));
|
|
|
|
// now if value is bool, toFloat() will hardly success in its
|
|
// conversion, so help out a bit
|
|
QString val = varNode.attribute(attrValue);
|
|
bool ok = false;
|
|
if (val == "TRUE" || val == "FALSE")
|
|
result[i] = (val == "TRUE") ? 1.0f : 0.0f;
|
|
else {
|
|
result[i] = val.toFloat(&ok);
|
|
}
|
|
}
|
|
|
|
if (!varNode.attribute("MIN").isNull())
|
|
result[16] = varNode.attribute("MIN").toFloat();
|
|
if (!varNode.attribute("MAX").isNull())
|
|
result[17] = varNode.attribute("MAX").toFloat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QString RfxParser::TextureFromRfx(const QString& VarName,
|
|
RfxUniform::UniformType VarType)
|
|
{
|
|
QString elementName = UniformToRfx[VarType];
|
|
QString filePath = "/not/found";
|
|
|
|
QDomElement varNode;
|
|
QDomNodeList candidates = root.elementsByTagName(elementName);
|
|
|
|
int i;
|
|
for (i = 0; i < candidates.size(); ++i) {
|
|
varNode = candidates.at(i).toElement();
|
|
if (varNode.attribute("NAME") == VarName) {
|
|
|
|
// start from rfx file directory
|
|
QDir fDir(QFileInfo(*rmShader).absolutePath());
|
|
|
|
// ... get relative path to texture file
|
|
QString fName(varNode.attribute("FILE_NAME").replace('\\', '/'));
|
|
|
|
// The textures with the special name TEXTURE0.png
|
|
// are interpreted as 'the current texture of the mesh'
|
|
// to allow a seamless integration of the shaders with
|
|
// the textured mesh of meshlab.
|
|
if(fName==QString("TEXTURE0.PNG"))
|
|
{
|
|
QFileInfo thefile(meshTextureName);
|
|
if(thefile.exists()) qDebug("The texture %s exists.", qPrintable(meshTextureName));
|
|
filePath = thefile.absoluteFilePath();
|
|
qDebug("Using the texture of the mesh %s",qPrintable(filePath));
|
|
}
|
|
else
|
|
{
|
|
// then join together and return the absolute path
|
|
QFileInfo thefile(fDir, fName);
|
|
filePath = thefile.absoluteFilePath();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if nothing was found and we're looking for a sampler2D, maybe it's
|
|
// a RmRenderableTexture
|
|
// ***
|
|
// note that we quit previous loop with break, so the only reason
|
|
// for i being equal candidates.size() is that nothing was found
|
|
if (i == candidates.size() && VarType == RfxUniform::SAMPLER2D) {
|
|
int rt = rfxShader->FindRT(VarName);
|
|
|
|
// convention: if texture is a renderable texture, pass
|
|
// "RT:<index in list>" string to uniform class
|
|
if (rt != -1)
|
|
filePath = "RT:" + QString().setNum(rt);
|
|
}
|
|
|
|
return filePath;
|
|
}
|
|
|
|
QList<RfxState*> RfxParser::ParseGLStates(QDomNodeList statelist,
|
|
RfxState::StateType statetype)
|
|
{
|
|
QList<RfxState*> glStates;
|
|
|
|
for (int i = 0; i < statelist.size(); ++i) {
|
|
QDomElement stateEl = statelist.at(i).toElement();
|
|
RfxState *glstate = new RfxState(statetype);
|
|
glstate->SetState(stateEl.attribute("STATE").toInt());
|
|
glstate->SetValue(stateEl.attribute("VALUE").toLong());
|
|
|
|
glStates.append(glstate);
|
|
}
|
|
|
|
return glStates;
|
|
}
|
|
|
|
void RfxParser::ParseUniforms(const QString &source)
|
|
{
|
|
// basically we're looking for declarations like this:
|
|
// uniform <type> <identifier> ;
|
|
|
|
QString txtSource = source;
|
|
int position = 0;
|
|
int unif;
|
|
while ((unif = txtSource.indexOf("uniform", position)) != -1) {
|
|
int semicol = txtSource.indexOf(";", unif);
|
|
int declLen = semicol - unif;
|
|
QStringList decl = txtSource.mid(unif, declLen).split(QRegExp("\\s+"));
|
|
position = semicol;
|
|
|
|
// store mappings between uniform declarations and their types
|
|
// for easy lookup.
|
|
// (eg: uniformType["MyTexture"] = "sampler2D")
|
|
enum declContent {UNIFORM, TYPE, IDENTIFIER};
|
|
uniformType[decl[IDENTIFIER]] = decl[TYPE];
|
|
}
|
|
}
|
|
|
|
/*
|
|
Verifies if the shader sources contains some special attributes.
|
|
For each special attribute declared creates a new instance of SpecialAttribute and appends it in the GLPass.
|
|
@param source the source of the shader.
|
|
@param pass the GLPass.
|
|
*/
|
|
void RfxParser::ParseAttributes(const QString &source, RfxGLPass *pass)
|
|
{
|
|
QString txtSource = source;
|
|
int position = 0;
|
|
int attrib;
|
|
while ((attrib = txtSource.indexOf("attribute", position)) != -1) {
|
|
int semicol = txtSource.indexOf(";", attrib);
|
|
int declLen = semicol - attrib;
|
|
QStringList decl = txtSource.mid(attrib, declLen).split(QRegExp("\\s+|\\,"));
|
|
position = semicol;
|
|
QString temp;
|
|
for (int i =2; i < decl.size(); ++i){
|
|
temp = decl.at(i);
|
|
//qDebug("temp : ",temp);
|
|
if(RfxSpecialAttribute::getSpecialType(temp) != RfxSpecialAttribute::NONE)
|
|
pass->AddSpecialAttribute(new RfxSpecialAttribute(temp));
|
|
}
|
|
}
|
|
} |