// Glew include #include // TnzCore includes #include "tstream.h" #include "tmsgcore.h" // Qt includes #include #include #include #include "stdfx/shaderinterface.h" //========================================================= PERSIST_IDENTIFIER(ShaderInterface, "ShaderInterface") PERSIST_IDENTIFIER(ShaderInterface::ParameterConcept, "ShaderInterface::ParameterConcept") PERSIST_IDENTIFIER(ShaderInterface::Parameter, "ShaderInterface::Parameter") PERSIST_IDENTIFIER(ShaderInterface::ShaderData, "ShaderInterface::ShaderData") //********************************************************************** // Local Namespace stuff //********************************************************************** namespace { // Filescope declarations typedef std::pair CompiledShader; struct CaselessCompare { const QString &m_str; CaselessCompare(const QString &str) : m_str(str) {} bool operator()(const QString &str) const { return (m_str.compare(str, Qt::CaseInsensitive) == 0); } }; // Filescope variables const static QString l_typeNames[ShaderInterface::TYPESCOUNT] = { "", "bool", "float", "vec2", "vec3", "vec4", "int", "ivec2", "ivec3", "ivec4", "rgba", "rgb"}; const static QString l_conceptNames[ShaderInterface::CONCEPTSCOUNT] = { "none", "percent", "length", "angle", "point", "radius_ui", "width_ui", "angle_ui", "point_ui", "xy_ui", "vector_ui", "polar_ui", "size_ui", "quad_ui", "rect_ui", "compass_ui", "compass_spin_ui"}; const static QString l_hwtNames[ShaderInterface::HWTCOUNT] = {"none", "any", "isotropic"}; enum Names { MAIN_PROGRAM, INPUT_PORTS, INPUT_PORT, PORTS_PROGRAM, PARAMETERS, PARAMETER, NAME, PROGRAM_FILE, CONCEPT, DEFAULT_, RANGE, HANDLED_WORLD_TRANSFORMS, BBOX_PROGRAM, NAMESCOUNT }; const static std::string l_names[NAMESCOUNT] = { "MainProgram", "InputPorts", "InputPort", "PortsProgram", "Parameters", "Parameter", "Name", "ProgramFile", "Concept", "Default", "Range", "HandledWorldTransforms", "BBoxProgram"}; // Filescope functions inline bool loadShader(QOpenGLShader::ShaderType type, const TFilePath &fp, CompiledShader &cs) { QOpenGLShader *shader = new QOpenGLShader(type, cs.first); const QString &qfp = QString::fromStdWString(fp.getWideString()); QFileInfo shaderFileInfo(qfp); cs.second = shaderFileInfo.lastModified(); return shader->compileSourceFile(qfp) && cs.first->addShader(shader); } void dumpError(TIStream &is, const std::wstring &err = std::wstring()) { DVGui::info("Error reading " + QString::fromStdWString(is.getFilePath().getLevelNameW()) + " (line " + QString::number(is.getLine()) + ")" + (err.empty() ? QString() : QString::fromStdWString(L": " + err))); } void skipTag(TIStream &is, const std::string &tagName) { DVGui::info("Error reading " + QString::fromStdWString(is.getFilePath().getLevelNameW()) + " (line " + QString::number(is.getLine()) + "): Unknown tag '<" + QString::fromStdString(tagName) + ">'"); is.skipCurrentTag(); } } // namespace //********************************************************************** // ShaderInterface implementation //********************************************************************** ShaderInterface::ShaderInterface() : m_hwt(ANY) {} //--------------------------------------------------------- void ShaderInterface::clear() { m_mainShader = m_portsShader = ShaderData(); m_parameters.clear(); } //--------------------------------------------------------- bool ShaderInterface::isValid() const { return m_mainShader.isValid(); } //--------------------------------------------------------- const std::vector &ShaderInterface::parameters() const { return m_parameters; } //--------------------------------------------------------- const std::vector &ShaderInterface::inputPorts() const { return m_ports; } //--------------------------------------------------------- const ShaderInterface::ShaderData &ShaderInterface::mainShader() const { return m_mainShader; } //--------------------------------------------------------- const ShaderInterface::ShaderData &ShaderInterface::inputPortsShader() const { return m_portsShader; } //--------------------------------------------------------- const ShaderInterface::ShaderData &ShaderInterface::bboxShader() const { return m_bboxShader; } //--------------------------------------------------------- ShaderInterface::HandledWorldTransformsType ShaderInterface::hwtType() const { return m_hwt; } //--------------------------------------------------------- std::pair ShaderInterface::makeProgram( const ShaderData &sd, int varyingsCount, const GLchar **varyingNames) const { CompiledShader result; if (!isValid()) return result; result.first = new QOpenGLShaderProgram; ::loadShader(sd.m_type, sd.m_path, result); if (varyingsCount > 0) glTransformFeedbackVaryings(result.first->programId(), varyingsCount, varyingNames, GL_INTERLEAVED_ATTRIBS); // NOTE: Since we'll be drawing a single vertex, GL_INTERLEAVED_ATTRIBS is // less restrictive than GL_SEPARATE_ATTRIBS. result.first->link(); return result; } //--------------------------------------------------------- void ShaderInterface::saveData(TOStream &os) { struct locals { inline static TFilePath getRelativePath(const TFilePath &file, const TFilePath &relTo) { QDir relToDir( QString::fromStdWString(relTo.getParentDir().getWideString())); QString relFileStr(relToDir.relativeFilePath( QString::fromStdWString(file.getWideString()))); return TFilePath(relFileStr.toStdWString()); } }; assert(isValid()); if (isValid()) { os.openChild(l_names[MAIN_PROGRAM]); os << m_mainShader; os.closeChild(); os.openChild(l_names[INPUT_PORTS]); { int i, iCount = int(m_ports.size()); for (i = 0; i != iCount; ++i) { os.openChild(l_names[INPUT_PORT]); os << m_ports[i]; os.closeChild(); } if (m_portsShader.isValid()) { os.openChild(l_names[PORTS_PROGRAM]); os << m_portsShader; os.closeChild(); } } os.closeChild(); if (m_bboxShader.isValid()) { os.openChild(l_names[BBOX_PROGRAM]); os << m_bboxShader; os.closeChild(); } if (m_hwt != ANY) { os.openChild(l_names[HANDLED_WORLD_TRANSFORMS]); os << l_names[m_hwt]; os.closeChild(); } os.openChild(l_names[PARAMETERS]); { int p, pCount = int(m_parameters.size()); for (p = 0; p != pCount; ++p) { os.openChild(l_names[PARAMETER]); os << m_parameters[p]; os.closeChild(); } } os.closeChild(); } } //--------------------------------------------------------- void ShaderInterface::loadData(TIStream &is) { struct locals { inline static TFilePath getAbsolutePath(const TFilePath &file, const TFilePath &relTo) { QDir relToDir( QString::fromStdWString(relTo.getParentDir().getWideString())); QString absFileStr(relToDir.absoluteFilePath( QString::fromStdWString(file.getWideString()))); return TFilePath(absFileStr.toStdWString()); } static bool nameMatch(const QString &name, const Parameter ¶m) { return (name == param.m_name); } }; std::string tagName; try { while (is.openChild(tagName)) { if (tagName == l_names[MAIN_PROGRAM]) { is >> m_mainShader; m_mainShader.m_type = QOpenGLShader::Fragment; is.closeChild(); } else if (tagName == l_names[INPUT_PORTS]) { while (is.openChild(tagName)) { if (tagName == l_names[INPUT_PORT]) { QString portName; is >> portName; m_ports.push_back(portName); is.closeChild(); } else if (tagName == l_names[PORTS_PROGRAM]) { is >> m_portsShader; m_portsShader.m_type = QOpenGLShader::Vertex; is.closeChild(); } else ::skipTag(is, tagName); } is.closeChild(); } else if (tagName == l_names[BBOX_PROGRAM]) { is >> m_bboxShader; m_bboxShader.m_type = QOpenGLShader::Vertex; is.closeChild(); } else if (tagName == l_names[HANDLED_WORLD_TRANSFORMS]) { QString hwtName; is >> hwtName; m_hwt = HandledWorldTransformsType( std::find_if(l_hwtNames, l_hwtNames + HWTCOUNT, ::CaselessCompare(hwtName)) - l_hwtNames); if (m_hwt == HWTCOUNT) { m_hwt = HWT_UNKNOWN; ::dumpError(is, L"Unrecognized HandledWorldTransforms type '" + hwtName.toStdWString() + L"'"); } is.closeChild(); } else if (tagName == l_names[PARAMETERS]) { while (is.openChild(tagName)) { if (tagName == l_names[PARAMETER]) { Parameter param; is >> param; m_parameters.push_back(param); is.closeChild(); } else ::skipTag(is, tagName); } is.closeChild(); } else if (tagName == l_names[CONCEPT]) { ParameterConcept concept; is >> concept; m_parConcepts.push_back(concept); is.closeChild(); } else ::skipTag(is, tagName); }; } catch (const TException &e) { ::dumpError(is, e.getMessage()); clear(); } catch (...) { ::dumpError(is); clear(); } } //********************************************************************** // ShaderInterface::ShaderData implementation //********************************************************************** void ShaderInterface::ShaderData::saveData(TOStream &os) { struct locals { inline static TFilePath getRelativePath(const TFilePath &file, const TFilePath &relTo) { QDir relToDir( QString::fromStdWString(relTo.getParentDir().getWideString())); QString relFileStr(relToDir.relativeFilePath( QString::fromStdWString(file.getWideString()))); return TFilePath(relFileStr.toStdWString()); } }; os.openChild(l_names[NAME]); os << m_name; os.closeChild(); os.openChild(l_names[PROGRAM_FILE]); os << locals::getRelativePath(m_path, os.getFilePath()); os.closeChild(); } //--------------------------------------------------------- void ShaderInterface::ShaderData::loadData(TIStream &is) { struct locals { inline static TFilePath getAbsolutePath(const TFilePath &file, const TFilePath &relTo) { QDir relToDir( QString::fromStdWString(relTo.getParentDir().getWideString())); QString absFileStr(relToDir.absoluteFilePath( QString::fromStdWString(file.getWideString()))); return TFilePath(absFileStr.toStdWString()); } }; std::string tagName; while (is.openChild(tagName)) { if (tagName == l_names[NAME]) is >> m_name, is.closeChild(); else if (tagName == l_names[PROGRAM_FILE]) { is >> m_path; m_path = locals::getAbsolutePath(m_path, is.getFilePath()); is.closeChild(); } else ::skipTag(is, tagName); } } //********************************************************************** // ShaderInterface::ParameterConcept implementation //********************************************************************** void ShaderInterface::ParameterConcept::saveData(TOStream &os) { os << l_conceptNames[m_type]; if (!m_label.isEmpty()) { os.openChild(l_names[NAME]); os << m_label; os.closeChild(); } int n, nCount = int(m_parameterNames.size()); for (n = 0; n != nCount; ++n) { os.openChild(l_names[PARAMETER]); os << m_parameterNames[n]; os.closeChild(); } } //--------------------------------------------------------- void ShaderInterface::ParameterConcept::loadData(TIStream &is) { // Read the concept type QString conceptName; is >> conceptName; m_type = ParameterConceptType(std::find_if(l_conceptNames, l_conceptNames + CONCEPTSCOUNT, ::CaselessCompare(conceptName)) - l_conceptNames); if (m_type == CONCEPTSCOUNT) { m_type = CONCEPT_NONE; ::dumpError( is, L"Unrecognized concept type '" + conceptName.toStdWString() + L"'"); } // Read any associated parameter names std::string tagName; while (is.openChild(tagName)) { if (tagName == l_names[PARAMETER]) { QString name; is >> name; m_parameterNames.push_back(name); is.closeChild(); } else if (tagName == l_names[NAME]) is >> m_label, is.closeChild(); else ::skipTag(is, tagName); } } //********************************************************************** // ShaderInterface::Parameter implementation //********************************************************************** void ShaderInterface::Parameter::saveData(TOStream &os) { os << l_typeNames[m_type] << m_name; os.openChild(l_names[CONCEPT]); os << m_concept; os.closeChild(); os.openChild(l_names[DEFAULT_]); switch (m_type) { case BOOL: os << (int)m_default.m_bool; break; case FLOAT: os << (double)m_default.m_float; break; case VEC2: os << (double)m_default.m_vec2[0] << (double)m_default.m_vec2[1]; break; case VEC3: os << (double)m_default.m_vec3[0] << (double)m_default.m_vec3[1] << (double)m_default.m_vec3[2]; break; case VEC4: os << (double)m_default.m_vec4[0] << (double)m_default.m_vec4[1] << (double)m_default.m_vec4[2] << (double)m_default.m_vec4[3]; break; case INT: os << m_default.m_int; break; case IVEC2: os << m_default.m_ivec2[0] << m_default.m_ivec2[1]; break; case IVEC3: os << m_default.m_ivec3[0] << m_default.m_ivec3[1] << m_default.m_ivec3[2]; break; case IVEC4: os << m_default.m_ivec4[0] << m_default.m_ivec4[1] << m_default.m_ivec4[2] << m_default.m_ivec4[3]; break; case RGBA: os << (int)m_default.m_rgba[0] << (int)m_default.m_rgba[1] << (int)m_default.m_rgba[2] << (int)m_default.m_rgba[3]; break; case RGB: os << (int)m_default.m_rgb[0] << (int)m_default.m_rgb[1] << (int)m_default.m_rgb[2]; break; default: break; } os.closeChild(); os.openChild(l_names[RANGE]); switch (m_type) { case FLOAT: os << (double)m_range[0].m_float << (double)m_range[1].m_float; break; case VEC2: os << (double)m_range[0].m_vec2[0] << (double)m_range[1].m_vec2[0] << (double)m_range[0].m_vec2[1] << (double)m_range[1].m_vec2[1]; break; case VEC3: os << (double)m_range[0].m_vec3[0] << (double)m_range[1].m_vec3[0] << (double)m_range[0].m_vec3[1] << (double)m_range[1].m_vec3[1] << (double)m_range[0].m_vec3[2] << (double)m_range[1].m_vec3[2]; break; case VEC4: os << (double)m_range[0].m_vec4[0] << (double)m_range[1].m_vec4[0] << (double)m_range[0].m_vec4[1] << (double)m_range[1].m_vec4[1] << (double)m_range[0].m_vec4[2] << (double)m_range[1].m_vec4[2] << (double)m_range[0].m_vec4[3] << (double)m_range[1].m_vec4[3]; break; case INT: os << m_range[0].m_int << m_range[1].m_int; break; case IVEC2: os << m_range[0].m_ivec2[0] << m_range[1].m_ivec2[0] << m_range[0].m_ivec2[1] << m_range[1].m_ivec2[1]; break; case IVEC3: os << m_range[0].m_ivec3[0] << m_range[1].m_ivec3[0] << m_range[0].m_ivec3[1] << m_range[1].m_ivec3[1] << m_range[0].m_ivec3[2] << m_range[1].m_ivec3[2]; break; case IVEC4: os << m_range[0].m_ivec4[0] << m_range[1].m_ivec4[0] << m_range[0].m_ivec4[1] << m_range[1].m_ivec4[1] << m_range[0].m_ivec4[2] << m_range[1].m_ivec4[2] << m_range[0].m_ivec4[3] << m_range[1].m_ivec4[3]; break; default: break; } os.closeChild(); } //--------------------------------------------------------- void ShaderInterface::Parameter::loadData(TIStream &is) { // Load type and name QString typeName; is >> typeName >> m_name; m_type = ShaderInterface::ParameterType( std::find_if(l_typeNames, l_typeNames + TYPESCOUNT, ::CaselessCompare(typeName)) - l_typeNames); if (m_type == TYPESCOUNT) throw TException(L"Unrecognized parameter type '" + typeName.toStdWString() + L"'"); // Load range std::string tagName; // Initialize default values m_concept = ParameterConcept(); switch (m_type) { case BOOL: m_default.m_bool = false; break; case FLOAT: m_default.m_float = 0.0; m_range[0].m_float = -(std::numeric_limits::max)(); m_range[1].m_float = (std::numeric_limits::max)(); break; case VEC2: m_default.m_vec2[0] = m_default.m_vec2[1] = 0.0; m_range[0].m_vec2[0] = m_range[0].m_vec2[1] = -(std::numeric_limits::max)(); m_range[1].m_vec2[0] = m_range[1].m_vec2[1] = (std::numeric_limits::max)(); break; case VEC3: m_default.m_vec3[0] = m_default.m_vec3[1] = m_default.m_vec3[1] = 0.0; m_range[0].m_vec3[0] = m_range[0].m_vec3[1] = m_range[0].m_vec3[2] = -(std::numeric_limits::max)(); m_range[1].m_vec3[0] = m_range[1].m_vec3[1] = m_range[1].m_vec3[2] = (std::numeric_limits::max)(); break; case VEC4: m_default.m_vec4[0] = m_default.m_vec4[1] = m_default.m_vec4[1] = m_default.m_vec4[1] = 0.0; m_range[0].m_vec4[0] = m_range[0].m_vec4[1] = m_range[0].m_vec4[2] = m_range[0].m_vec4[3] = -(std::numeric_limits::max)(); m_range[1].m_vec4[0] = m_range[1].m_vec4[1] = m_range[1].m_vec4[2] = m_range[1].m_vec4[3] = (std::numeric_limits::max)(); break; case INT: m_default.m_int = 0; m_range[0].m_int = -(std::numeric_limits::max)(); m_range[1].m_int = (std::numeric_limits::max)(); break; case IVEC2: m_default.m_ivec2[0] = m_default.m_ivec2[1] = 0; m_range[0].m_ivec2[0] = m_range[0].m_ivec2[1] = -(std::numeric_limits::max)(); m_range[1].m_ivec2[0] = m_range[1].m_ivec2[1] = (std::numeric_limits::max)(); break; case IVEC3: m_default.m_ivec3[0] = m_default.m_ivec3[1] = m_default.m_ivec3[1] = 0; m_range[0].m_ivec3[0] = m_range[0].m_ivec3[1] = m_range[0].m_ivec3[2] = -(std::numeric_limits::max)(); m_range[1].m_ivec3[0] = m_range[1].m_ivec3[1] = m_range[1].m_ivec3[2] = (std::numeric_limits::max)(); break; case IVEC4: m_default.m_ivec4[0] = m_default.m_ivec4[1] = m_default.m_ivec4[1] = m_default.m_ivec4[1] = 0; m_range[0].m_ivec4[0] = m_range[0].m_ivec4[1] = m_range[0].m_ivec4[2] = m_range[0].m_ivec4[3] = -(std::numeric_limits::max)(); m_range[1].m_ivec4[0] = m_range[1].m_ivec4[1] = m_range[1].m_ivec4[2] = m_range[1].m_ivec4[3] = (std::numeric_limits::max)(); break; case RGBA: m_default.m_rgba[0] = m_default.m_rgba[1] = m_default.m_rgba[2] = m_default.m_rgba[3] = 255; break; case RGB: m_default.m_rgb[0] = m_default.m_rgb[1] = m_default.m_rgb[2] = 255; break; default: break; } // Attempt loading range from file while (is.openChild(tagName)) { if (tagName == l_names[CONCEPT]) is >> m_concept, is.closeChild(); else if (tagName == l_names[DEFAULT_]) { switch (m_type) { case BOOL: { int val; is >> val, m_default.m_bool = val; break; } case FLOAT: { double val; is >> val, m_default.m_float = val; break; } case VEC2: { double val; is >> val, m_default.m_vec2[0] = val; is >> val, m_default.m_vec2[1] = val; break; } case VEC3: { double val; is >> val, m_default.m_vec3[0] = val; is >> val, m_default.m_vec3[1] = val; is >> val, m_default.m_vec3[2] = val; break; } case VEC4: { double val; is >> val, m_default.m_vec4[0] = val; is >> val, m_default.m_vec4[1] = val; is >> val, m_default.m_vec4[2] = val; is >> val, m_default.m_vec4[3] = val; break; } case INT: is >> m_default.m_int; break; case IVEC2: is >> m_default.m_ivec2[0] >> m_default.m_ivec2[1]; break; case IVEC3: is >> m_default.m_ivec3[0] >> m_default.m_ivec3[1] >> m_default.m_ivec3[2]; break; case IVEC4: is >> m_default.m_ivec4[0] >> m_default.m_ivec4[1] >> m_default.m_ivec4[2] >> m_default.m_ivec4[3]; break; case RGBA: { int val; is >> val, m_default.m_rgba[0] = val; is >> val, m_default.m_rgba[1] = val; is >> val, m_default.m_rgba[2] = val; is >> val, m_default.m_rgba[3] = val; break; } case RGB: { int val; is >> val, m_default.m_rgb[0] = val; is >> val, m_default.m_rgb[1] = val; is >> val, m_default.m_rgb[2] = val; break; } default: break; } is.closeChild(); } else if (tagName == l_names[RANGE]) { switch (m_type) { case FLOAT: { double val; is >> val, m_range[0].m_float = val; is >> val, m_range[1].m_float = val; break; } case VEC2: { double val; is >> val, m_range[0].m_vec2[0] = val; is >> val, m_range[1].m_vec2[0] = val; is >> val, m_range[0].m_vec2[1] = val; is >> val, m_range[1].m_vec2[1] = val; break; } case VEC3: { double val; is >> val, m_range[0].m_vec3[0] = val; is >> val, m_range[1].m_vec3[0] = val; is >> val, m_range[0].m_vec3[1] = val; is >> val, m_range[1].m_vec3[1] = val; is >> val, m_range[0].m_vec3[2] = val; is >> val, m_range[1].m_vec3[2] = val; break; } case VEC4: { double val; is >> val, m_range[0].m_vec4[0] = val; is >> val, m_range[1].m_vec4[0] = val; is >> val, m_range[0].m_vec4[1] = val; is >> val, m_range[1].m_vec4[1] = val; is >> val, m_range[0].m_vec4[2] = val; is >> val, m_range[1].m_vec4[2] = val; is >> val, m_range[0].m_vec4[3] = val; is >> val, m_range[1].m_vec4[3] = val; break; } case INT: is >> m_range[0].m_int >> m_range[1].m_int; break; case IVEC2: is >> m_range[0].m_ivec2[0] >> m_range[1].m_ivec2[0] >> m_range[0].m_ivec2[1] >> m_range[1].m_ivec2[1]; break; case IVEC3: is >> m_range[0].m_ivec3[0] >> m_range[1].m_ivec3[0] >> m_range[0].m_ivec3[1] >> m_range[1].m_ivec3[1] >> m_range[0].m_ivec3[2] >> m_range[1].m_ivec3[2]; break; case IVEC4: is >> m_range[0].m_ivec4[0] >> m_range[1].m_ivec4[0] >> m_range[0].m_ivec4[1] >> m_range[1].m_ivec4[1] >> m_range[0].m_ivec4[2] >> m_range[1].m_ivec4[2] >> m_range[0].m_ivec4[3] >> m_range[1].m_ivec4[3]; break; default: break; } is.closeChild(); } else ::skipTag(is, tagName); } // Post-process loaded data if (m_concept.m_label.isEmpty()) m_concept.m_label = m_name; }