moddable post-processing pipeline

This commit is contained in:
cody glassman 2022-05-13 18:58:00 -07:00
parent e7fb8b6fd8
commit 04843fed6d
130 changed files with 8645 additions and 608 deletions

View file

@ -82,6 +82,10 @@ add_component_dir (to_utf8
add_component_dir(esm attr common defs esmcommon reader records util luascripts format)
add_component_dir(fx pass technique lexer widgets stateupdater)
add_component_dir(std140 ubo)
add_component_dir (esm3
esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst

301
components/fx/lexer.cpp Normal file
View file

@ -0,0 +1,301 @@
#include "lexer.hpp"
#include <string_view>
#include <string>
#include <variant>
#include <optional>
#include <cstdint>
#include <array>
#include <cmath>
#include <exception>
#include <components/misc/stringops.hpp>
#include <components/debug/debuglog.hpp>
#include "types.hpp"
namespace fx
{
namespace Lexer
{
Lexer::Lexer(std::string_view buffer)
: mHead(buffer.data())
, mTail(mHead + buffer.length())
, mAbsolutePos(0)
, mColumn(0)
, mLine(0)
, mBuffer(buffer)
, mLastToken(Eof{})
{ }
Token Lexer::next()
{
if (mLookahead)
{
auto token = *mLookahead;
drop();
return token;
}
mLastToken = scanToken();
return mLastToken;
}
Token Lexer::peek()
{
if (!mLookahead)
mLookahead = scanToken();
return *mLookahead;
}
void Lexer::drop()
{
mLookahead = std::nullopt;
}
std::optional<std::string_view> Lexer::jump()
{
bool multi = false;
bool single = false;
auto start = mHead;
std::size_t level = 1;
mLastJumpBlock.line = mLine;
if (head() == '}')
{
mLastJumpBlock.content = {};
return mLastJumpBlock.content;
}
for (; mHead != mTail; advance())
{
if (head() == '\n')
{
mLine++;
mColumn = 0;
if (single)
{
single = false;
continue;
}
}
else if (multi && head() == '*' && peekChar('/'))
{
multi = false;
advance();
continue;
}
else if (multi || single)
{
continue;
}
else if (head() == '/' && peekChar('/'))
{
single = true;
advance();
continue;
}
else if (head() == '/' && peekChar('*'))
{
multi = true;
advance();
continue;
}
if (head() == '{')
level++;
else if (head() == '}')
level--;
if (level == 0)
{
mHead--;
auto sv = std::string_view{start, static_cast<std::string_view::size_type>(mHead + 1 - start)};
mLastJumpBlock.content = sv;
return sv;
}
}
mLastJumpBlock = {};
return std::nullopt;
}
Lexer::Block Lexer::getLastJumpBlock() const
{
return mLastJumpBlock;
}
[[noreturn]] void Lexer::error(const std::string& msg)
{
throw LexerException(Misc::StringUtils::format("Line %zu Col %zu. %s", mLine + 1, mColumn, msg));
}
void Lexer::advance()
{
mAbsolutePos++;
mHead++;
mColumn++;
}
char Lexer::head()
{
return *mHead;
}
bool Lexer::peekChar(char c)
{
if (mHead == mTail)
return false;
return *(mHead + 1) == c;
}
Token Lexer::scanToken()
{
while (true)
{
if (mHead == mTail)
return {Eof{}};
if (head() == '\n')
{
mLine++;
mColumn = 0;
}
if (!std::isspace(head()))
break;
advance();
}
if (head() == '\"')
return scanStringLiteral();
if (std::isalpha(head()))
return scanLiteral();
if (std::isdigit(head()) || head() == '.' || head() == '-')
return scanNumber();
switch(head())
{
case '=':
advance();
return {Equal{}};
case '{':
advance();
return {Open_bracket{}};
case '}':
advance();
return {Close_bracket{}};
case '(':
advance();
return {Open_Parenthesis{}};
case ')':
advance();
return {Close_Parenthesis{}};
case '\"':
advance();
return {Quote{}};
case ':':
advance();
return {Colon{}};
case ';':
advance();
return {SemiColon{}};
case '|':
advance();
return {VBar{}};
case ',':
advance();
return {Comma{}};
default:
error(Misc::StringUtils::format("unexpected token <%c>", head()));
}
}
Token Lexer::scanLiteral()
{
auto start = mHead;
advance();
while (mHead != mTail && (std::isalnum(head()) || head() == '_'))
advance();
std::string_view value{start, static_cast<std::string_view::size_type>(mHead - start)};
if (value == "shared") return Shared{};
if (value == "technique") return Technique{};
if (value == "main_pass") return Main_Pass{};
if (value == "render_target") return Render_Target{};
if (value == "vertex") return Vertex{};
if (value == "fragment") return Fragment{};
if (value == "compute") return Compute{};
if (value == "sampler_1d") return Sampler_1D{};
if (value == "sampler_2d") return Sampler_2D{};
if (value == "sampler_3d") return Sampler_3D{};
if (value == "uniform_bool") return Uniform_Bool{};
if (value == "uniform_float") return Uniform_Float{};
if (value == "uniform_int") return Uniform_Int{};
if (value == "uniform_vec2") return Uniform_Vec2{};
if (value == "uniform_vec3") return Uniform_Vec3{};
if (value == "uniform_vec4") return Uniform_Vec4{};
if (value == "true") return True{};
if (value == "false") return False{};
if (value == "vec2") return Vec2{};
if (value == "vec3") return Vec3{};
if (value == "vec4") return Vec4{};
return Literal{value};
}
Token Lexer::scanStringLiteral()
{
advance(); // consume quote
auto start = mHead;
bool terminated = false;
for (; mHead != mTail; advance())
{
if (head() == '\"')
{
terminated = true;
advance();
break;
}
}
if (!terminated)
error("unterminated string");
return String{{start, static_cast<std::string_view::size_type>(mHead - start - 1)}};
}
Token Lexer::scanNumber()
{
double buffer;
char* endPtr;
buffer = std::strtod(mHead, &endPtr);
if (endPtr == nullptr)
error("critical error while parsing number");
const char* tmp = mHead;
mHead = endPtr;
for (; tmp != endPtr; ++tmp)
{
if ((*tmp == '.'))
return Float{static_cast<float>(buffer)};
}
return Integer{static_cast<int>(buffer)};
}
}
}

75
components/fx/lexer.hpp Normal file
View file

@ -0,0 +1,75 @@
#ifndef OPENMW_COMPONENTS_FX_LEXER_H
#define OPENMW_COMPONENTS_FX_LEXER_H
#include <string_view>
#include <string>
#include <variant>
#include <optional>
#include <cstdint>
#include <stdexcept>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/Vec4f>
#include "lexer_types.hpp"
namespace fx
{
namespace Lexer
{
struct LexerException : std::runtime_error
{
LexerException(const std::string& message) : std::runtime_error(message) {}
LexerException(const char* message) : std::runtime_error(message) {}
};
class Lexer
{
public:
struct Block
{
int line;
std::string_view content;
};
Lexer(std::string_view buffer);
Lexer() = delete;
Token next();
Token peek();
// Jump ahead to next uncommented closing bracket at level zero. Assumes the head is at an opening bracket.
// Returns the contents of the block excluding the brackets and places cursor at closing bracket.
std::optional<std::string_view> jump();
Block getLastJumpBlock() const;
[[noreturn]] void error(const std::string& msg);
private:
void drop();
void advance();
char head();
bool peekChar(char c);
Token scanToken();
Token scanLiteral();
Token scanStringLiteral();
Token scanNumber();
const char* mHead;
const char* mTail;
std::size_t mAbsolutePos;
std::size_t mColumn;
std::size_t mLine;
std::string_view mBuffer;
Token mLastToken;
std::optional<Token> mLookahead;
Block mLastJumpBlock;
};
}
}
#endif

View file

@ -0,0 +1,56 @@
#ifndef OPENMW_COMPONENTS_FX_LEXER_TYPES_H
#define OPENMW_COMPONENTS_FX_LEXER_TYPES_H
#include <variant>
#include <string_view>
namespace fx
{
namespace Lexer
{
struct Float { inline static constexpr std::string_view repr = "float"; float value = 0.0;};
struct Integer { inline static constexpr std::string_view repr = "integer"; int value = 0;};
struct Boolean { inline static constexpr std::string_view repr = "boolean"; bool value = false;};
struct Literal { inline static constexpr std::string_view repr = "literal"; std::string_view value;};
struct String { inline static constexpr std::string_view repr = "string"; std::string_view value;};
struct Shared { inline static constexpr std::string_view repr = "shared"; };
struct Vertex { inline static constexpr std::string_view repr = "vertex"; };
struct Fragment { inline static constexpr std::string_view repr = "fragment"; };
struct Compute { inline static constexpr std::string_view repr = "compute"; };
struct Technique { inline static constexpr std::string_view repr = "technique"; };
struct Main_Pass { inline static constexpr std::string_view repr = "main_pass"; };
struct Render_Target { inline static constexpr std::string_view repr = "render_target"; };
struct Sampler_1D { inline static constexpr std::string_view repr = "sampler_1d"; };
struct Sampler_2D { inline static constexpr std::string_view repr = "sampler_2d"; };
struct Sampler_3D { inline static constexpr std::string_view repr = "sampler_3d"; };
struct Uniform_Bool { inline static constexpr std::string_view repr = "uniform_bool"; };
struct Uniform_Float { inline static constexpr std::string_view repr = "uniform_float"; };
struct Uniform_Int { inline static constexpr std::string_view repr = "uniform_int"; };
struct Uniform_Vec2 { inline static constexpr std::string_view repr = "uniform_vec2"; };
struct Uniform_Vec3 { inline static constexpr std::string_view repr = "uniform_vec3"; };
struct Uniform_Vec4 { inline static constexpr std::string_view repr = "uniform_vec4"; };
struct Eof { inline static constexpr std::string_view repr = "eof"; };
struct Equal { inline static constexpr std::string_view repr = "equal"; };
struct Open_bracket { inline static constexpr std::string_view repr = "open_bracket"; };
struct Close_bracket { inline static constexpr std::string_view repr = "close_bracket"; };
struct Open_Parenthesis { inline static constexpr std::string_view repr = "open_parenthesis"; };
struct Close_Parenthesis{ inline static constexpr std::string_view repr = "close_parenthesis"; };
struct Quote { inline static constexpr std::string_view repr = "quote"; };
struct SemiColon { inline static constexpr std::string_view repr = "semicolon"; };
struct Comma { inline static constexpr std::string_view repr = "comma"; };
struct VBar { inline static constexpr std::string_view repr = "vbar"; };
struct Colon { inline static constexpr std::string_view repr = "colon"; };
struct True { inline static constexpr std::string_view repr = "true"; };
struct False { inline static constexpr std::string_view repr = "false"; };
struct Vec2 { inline static constexpr std::string_view repr = "vec2"; };
struct Vec3 { inline static constexpr std::string_view repr = "vec3"; };
struct Vec4 { inline static constexpr std::string_view repr = "vec4"; };
using Token = std::variant<Float, Integer, Boolean, String, Literal, Equal, Open_bracket, Close_bracket, Open_Parenthesis,
Close_Parenthesis, Quote, SemiColon, Comma, VBar, Colon, Shared, Technique, Render_Target, Vertex, Fragment,
Compute, Sampler_1D, Sampler_2D, Sampler_3D, Uniform_Bool, Uniform_Float, Uniform_Int, Uniform_Vec2, Uniform_Vec3, Uniform_Vec4,
True, False, Vec2, Vec3, Vec4, Main_Pass, Eof>;
}
}
#endif

View file

@ -0,0 +1,133 @@
#ifndef OPENMW_COMPONENTS_FX_PARSE_CONSTANTS_H
#define OPENMW_COMPONENTS_FX_PARSE_CONSTANTS_H
#include <array>
#include <string_view>
#include <osg/Texture>
#include <osg/Image>
#include <osg/BlendFunc>
#include <osg/BlendEquation>
#include <components/sceneutil/color.hpp>
#include "technique.hpp"
namespace fx
{
namespace constants
{
constexpr std::array<std::pair<std::string_view, fx::FlagsType>, 6> TechniqueFlag = {{
{"disable_interiors" , Technique::Flag_Disable_Interiors},
{"disable_exteriors" , Technique::Flag_Disable_Exteriors},
{"disable_underwater" , Technique::Flag_Disable_Underwater},
{"disable_abovewater" , Technique::Flag_Disable_Abovewater},
{"disable_sunglare" , Technique::Flag_Disable_SunGlare},
{"hidden" , Technique::Flag_Hidden}
}};
constexpr std::array<std::pair<std::string_view, int>, 6> SourceFormat = {{
{"red" , GL_RED},
{"rg" , GL_RG},
{"rgb" , GL_RGB},
{"bgr" , GL_BGR},
{"rgba", GL_RGBA},
{"bgra", GL_BGRA},
}};
constexpr std::array<std::pair<std::string_view, int>, 9> SourceType = {{
{"byte" , GL_BYTE},
{"unsigned_byte" , GL_UNSIGNED_BYTE},
{"short" , GL_SHORT},
{"unsigned_short" , GL_UNSIGNED_SHORT},
{"int" , GL_INT},
{"unsigned_int" , GL_UNSIGNED_INT},
{"unsigned_int_24_8", GL_UNSIGNED_INT_24_8},
{"float" , GL_FLOAT},
{"double" , GL_DOUBLE},
}};
constexpr std::array<std::pair<std::string_view, int>, 16> InternalFormat = {{
{"red" , GL_RED},
{"r16f" , GL_R16F},
{"r32f" , GL_R32F},
{"rg" , GL_RG},
{"rg16f" , GL_RG16F},
{"rg32f" , GL_RG32F},
{"rgb" , GL_RGB},
{"rgb16f" , GL_RGB16F},
{"rgb32f" , GL_RGB32F},
{"rgba" , GL_RGBA},
{"rgba16f" , GL_RGBA16F},
{"rgba32f" , GL_RGBA32F},
{"depth_component16" , GL_DEPTH_COMPONENT16},
{"depth_component24" , GL_DEPTH_COMPONENT24},
{"depth_component32" , GL_DEPTH_COMPONENT32},
{"depth_component32f", GL_DEPTH_COMPONENT32F}
}};
constexpr std::array<std::pair<std::string_view, osg::Texture::InternalFormatMode>, 13> Compression = {{
{"auto" , osg::Texture::USE_USER_DEFINED_FORMAT},
{"arb" , osg::Texture::USE_ARB_COMPRESSION},
{"s3tc_dxt1" , osg::Texture::USE_S3TC_DXT1_COMPRESSION},
{"s3tc_dxt3" , osg::Texture::USE_S3TC_DXT3_COMPRESSION},
{"s3tc_dxt5" , osg::Texture::USE_S3TC_DXT5_COMPRESSION},
{"pvrtc_2bpp" , osg::Texture::USE_PVRTC_2BPP_COMPRESSION},
{"pvrtc_4bpp" , osg::Texture::USE_PVRTC_4BPP_COMPRESSION},
{"etc" , osg::Texture::USE_ETC_COMPRESSION},
{"etc2" , osg::Texture::USE_ETC2_COMPRESSION},
{"rgtc1" , osg::Texture::USE_RGTC1_COMPRESSION},
{"rgtc2" , osg::Texture::USE_RGTC2_COMPRESSION},
{"s3tc_dxt1c" , osg::Texture::USE_S3TC_DXT1c_COMPRESSION},
{"s3tc_dxt1a" , osg::Texture::USE_S3TC_DXT1a_COMPRESSION}
}};
constexpr std::array<std::pair<std::string_view, osg::Texture::WrapMode>, 6> WrapMode = {{
{"clamp" , osg::Texture::CLAMP},
{"clamp_to_edge" , osg::Texture::CLAMP_TO_EDGE},
{"clamp_to_border", osg::Texture::CLAMP_TO_BORDER},
{"repeat" , osg::Texture::REPEAT},
{"mirror" , osg::Texture::MIRROR}
}};
constexpr std::array<std::pair<std::string_view, osg::Texture::FilterMode>, 6> FilterMode = {{
{"linear" , osg::Texture::LINEAR},
{"linear_mipmap_linear" , osg::Texture::LINEAR_MIPMAP_LINEAR},
{"linear_mipmap_nearest" , osg::Texture::LINEAR_MIPMAP_NEAREST},
{"nearest" , osg::Texture::NEAREST},
{"nearest_mipmap_linear" , osg::Texture::NEAREST_MIPMAP_LINEAR},
{"nearest_mipmap_nearest", osg::Texture::NEAREST_MIPMAP_NEAREST}
}};
constexpr std::array<std::pair<std::string_view, osg::BlendFunc::BlendFuncMode>, 15> BlendFunc = {{
{"dst_alpha" , osg::BlendFunc::DST_ALPHA},
{"dst_color" , osg::BlendFunc::DST_COLOR},
{"one" , osg::BlendFunc::ONE},
{"one_minus_dst_alpha" , osg::BlendFunc::ONE_MINUS_DST_ALPHA},
{"one_minus_dst_color" , osg::BlendFunc::ONE_MINUS_DST_COLOR},
{"one_minus_src_alpha" , osg::BlendFunc::ONE_MINUS_SRC_ALPHA},
{"one_minus_src_color" , osg::BlendFunc::ONE_MINUS_SRC_COLOR},
{"src_alpha" , osg::BlendFunc::SRC_ALPHA},
{"src_alpha_saturate" , osg::BlendFunc::SRC_ALPHA_SATURATE},
{"src_color" , osg::BlendFunc::SRC_COLOR},
{"constant_color" , osg::BlendFunc::CONSTANT_COLOR},
{"one_minus_constant_color" , osg::BlendFunc::ONE_MINUS_CONSTANT_COLOR},
{"constant_alpha" , osg::BlendFunc::CONSTANT_ALPHA},
{"one_minus_constant_alpha" , osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
{"zero" , osg::BlendFunc::ZERO}
}};
constexpr std::array<std::pair<std::string_view, osg::BlendEquation::Equation>, 8> BlendEquation = {{
{"rgba_min" , osg::BlendEquation::RGBA_MIN},
{"rgba_max" , osg::BlendEquation::RGBA_MAX},
{"alpha_min" , osg::BlendEquation::ALPHA_MIN},
{"alpha_max" , osg::BlendEquation::ALPHA_MAX},
{"logic_op" , osg::BlendEquation::LOGIC_OP},
{"add" , osg::BlendEquation::FUNC_ADD},
{"subtract" , osg::BlendEquation::FUNC_SUBTRACT},
{"reverse_subtract" , osg::BlendEquation::FUNC_REVERSE_SUBTRACT}
}};
}
}
#endif

253
components/fx/pass.cpp Normal file
View file

@ -0,0 +1,253 @@
#include "pass.hpp"
#include <unordered_set>
#include <string>
#include <sstream>
#include <osg/Program>
#include <osg/Shader>
#include <osg/State>
#include <osg/StateSet>
#include <osg/BindImageTexture>
#include <osg/FrameBufferObject>
#include <components/misc/stringops.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/clearcolor.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/stereo/multiview.hpp>
#include "technique.hpp"
#include "stateupdater.hpp"
namespace
{
constexpr char s_DefaultVertex[] = R"GLSL(
#if OMW_USE_BINDINGS
omw_In vec2 omw_Vertex;
#endif
omw_Out vec2 omw_TexCoord;
void main()
{
omw_Position = vec4(omw_Vertex.xy, 0.0, 1.0);
omw_TexCoord = omw_Position.xy * 0.5 + 0.5;
})GLSL";
}
namespace fx
{
Pass::Pass(Pass::Type type, Pass::Order order, bool ubo)
: mCompiled(false)
, mType(type)
, mOrder(order)
, mLegacyGLSL(true)
, mUBO(ubo)
{
}
std::string Pass::getPassHeader(Technique& technique, std::string_view preamble, bool fragOut)
{
std::string header = R"GLSL(
#version @version @profile
@extensions
@uboStruct
#define OMW_REVERSE_Z @reverseZ
#define OMW_RADIAL_FOG @radialFog
#define OMW_HDR @hdr
#define OMW_NORMALS @normals
#define OMW_USE_BINDINGS @useBindings
#define OMW_MULTIVIEW @multiview
#define omw_In @in
#define omw_Out @out
#define omw_Position @position
#define omw_Texture1D @texture1D
#define omw_Texture2D @texture2D
#define omw_Texture3D @texture3D
#define omw_Vertex @vertex
#define omw_FragColor @fragColor
@fragBinding
uniform @builtinSampler omw_SamplerLastShader;
uniform @builtinSampler omw_SamplerLastPass;
uniform @builtinSampler omw_SamplerDepth;
uniform @builtinSampler omw_SamplerNormals;
#if @ubo
layout(std140) uniform _data { _omw_data omw; };
#else
uniform _omw_data omw;
#endif
float omw_GetDepth(vec2 uv)
{
#if OMW_MULTIVIEW
float depth = omw_Texture2D(omw_SamplerDepth, vec3(uv, gl_ViewID_OVR)).r;
#else
float depth = omw_Texture2D(omw_SamplerDepth, uv).r;
#endif
#if OMW_REVERSE_Z
return 1.0 - depth;
#else
return depth;
#endif
}
vec4 omw_GetLastShader(vec2 uv)
{
#if OMW_MULTIVIEW
return omw_Texture2D(omw_SamplerLastShader, vec3(uv, gl_ViewID_OVR));
#else
return omw_Texture2D(omw_SamplerLastShader, uv);
#endif
}
vec4 omw_GetLastPass(vec2 uv)
{
#if OMW_MULTIVIEW
return omw_Texture2D(omw_SamplerLastPass, vec3(uv, gl_ViewID_OVR));
#else
return omw_Texture2D(omw_SamplerLastPass, uv);
#endif
}
vec3 omw_GetNormals(vec2 uv)
{
#if OMW_MULTIVIEW
return omw_Texture2D(omw_SamplerNormals, vec3(uv, gl_ViewID_OVR)).rgb * 2.0 - 1.0;
#else
return omw_Texture2D(omw_SamplerNormals, uv).rgb * 2.0 - 1.0;
#endif
}
#if OMW_HDR
uniform sampler2D omw_EyeAdaptation;
#endif
float omw_GetEyeAdaptation()
{
#if OMW_HDR
return omw_Texture2D(omw_EyeAdaptation, vec2(0.5, 0.5)).r;
#else
return 1.0;
#endif
}
)GLSL";
std::stringstream extBlock;
for (const auto& extension : technique.getGLSLExtensions())
extBlock << "#ifdef " << extension << '\n' << "\t#extension " << extension << ": enable" << '\n' << "#endif" << '\n';
const std::vector<std::pair<std::string,std::string>> defines = {
{"@version", std::to_string(technique.getGLSLVersion())},
{"@multiview", Stereo::getMultiview() ? "1" : "0"},
{"@builtinSampler", Stereo::getMultiview() ? "sampler2DArray" : "sampler2D"},
{"@profile", technique.getGLSLProfile()},
{"@extensions", extBlock.str()},
{"@uboStruct", StateUpdater::getStructDefinition()},
{"@ubo", mUBO ? "1" : "0"},
{"@normals", technique.getNormals() ? "1" : "0"},
{"@reverseZ", SceneUtil::AutoDepth::isReversed() ? "1" : "0"},
{"@radialFog", Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0"},
{"@hdr", technique.getHDR() ? "1" : "0"},
{"@in", mLegacyGLSL ? "varying" : "in"},
{"@out", mLegacyGLSL ? "varying" : "out"},
{"@position", "gl_Position"},
{"@texture1D", mLegacyGLSL ? "texture1D" : "texture"},
{"@texture2D", mLegacyGLSL ? "texture2D" : "texture"},
{"@texture3D", mLegacyGLSL ? "texture3D" : "texture"},
{"@vertex", mLegacyGLSL ? "gl_Vertex" : "_omw_Vertex"},
{"@fragColor", mLegacyGLSL ? "gl_FragColor" : "_omw_FragColor"},
{"@useBindings", mLegacyGLSL ? "0" : "1"},
{"@fragBinding", mLegacyGLSL ? "" : "out vec4 omw_FragColor;"}
};
for (const auto& [define, value]: defines)
for (size_t pos = header.find(define); pos != std::string::npos; pos = header.find(define))
header.replace(pos, define.size(), value);
for (auto& uniform : technique.getUniformMap())
if (auto glsl = uniform->getGLSL())
header.append(glsl.value());
header.append(preamble);
return header;
}
void Pass::prepareStateSet(osg::StateSet* stateSet, const std::string& name) const
{
osg::ref_ptr<osg::Program> program = new osg::Program;
if (mType == Type::Pixel)
{
program->addShader(new osg::Shader(*mVertex));
program->addShader(new osg::Shader(*mFragment));
}
else if (mType == Type::Compute)
{
program->addShader(new osg::Shader(*mCompute));
}
if (mUBO)
program->addBindUniformBlock("_data", static_cast<int>(Resource::SceneManager::UBOBinding::PostProcessor));
program->setName(name);
if (!mLegacyGLSL)
{
program->addBindFragDataLocation("_omw_FragColor", 0);
program->addBindAttribLocation("_omw_Vertex", 0);
}
stateSet->setAttribute(program);
if (mBlendSource && mBlendDest)
stateSet->setAttribute(new osg::BlendFunc(mBlendSource.value(), mBlendDest.value()));
if (mBlendEq)
stateSet->setAttribute(new osg::BlendEquation(mBlendEq.value()));
if (mClearColor)
stateSet->setAttribute(new SceneUtil::ClearColor(mClearColor.value(), GL_COLOR_BUFFER_BIT));
}
void Pass::dirty()
{
mVertex = nullptr;
mFragment = nullptr;
mCompute = nullptr;
mCompiled = false;
}
void Pass::compile(Technique& technique, std::string_view preamble)
{
if (mCompiled)
return;
mLegacyGLSL = technique.getGLSLVersion() != 330;
if (mType == Type::Pixel)
{
if (!mVertex)
mVertex = new osg::Shader(osg::Shader::VERTEX, s_DefaultVertex);
mVertex->setShaderSource(getPassHeader(technique, preamble).append(mVertex->getShaderSource()));
mFragment->setShaderSource(getPassHeader(technique, preamble, true).append(mFragment->getShaderSource()));
mVertex->setName(mName);
mFragment->setName(mName);
}
else if (mType == Type::Compute)
{
mCompute->setShaderSource(getPassHeader(technique, preamble).append(mCompute->getShaderSource()));
mCompute->setName(mName);
}
mCompiled = true;
}
}

79
components/fx/pass.hpp Normal file
View file

@ -0,0 +1,79 @@
#ifndef OPENMW_COMPONENTS_FX_PASS_H
#define OPENMW_COMPONENTS_FX_PASS_H
#include <array>
#include <string>
#include <sstream>
#include <cstdint>
#include <unordered_set>
#include <optional>
#include <osg/Timer>
#include <osg/Program>
#include <osg/Shader>
#include <osg/State>
#include <osg/Texture2D>
#include <osg/BlendEquation>
#include <osg/BlendFunc>
namespace fx
{
class Technique;
class Pass
{
public:
enum class Order
{
Forward,
Post
};
enum class Type
{
None,
Pixel,
Compute
};
friend class Technique;
Pass(Type type=Type::Pixel, Order order=Order::Post, bool ubo = false);
void compile(Technique& technique, std::string_view preamble);
std::string_view getTarget() const { return mTarget; }
void prepareStateSet(osg::StateSet* stateSet, const std::string& name) const;
std::string getName() const { return mName; }
void dirty();
private:
std::string getPassHeader(Technique& technique, std::string_view preamble, bool fragOut = false);
bool mCompiled;
osg::ref_ptr<osg::Shader> mVertex;
osg::ref_ptr<osg::Shader> mFragment;
osg::ref_ptr<osg::Shader> mCompute;
Type mType;
Order mOrder;
std::string mName;
bool mLegacyGLSL;
bool mUBO;
bool mSupportsNormals;
std::string_view mTarget;
std::optional<osg::Vec4f> mClearColor;
std::optional<osg::BlendFunc::BlendFuncMode> mBlendSource;
std::optional<osg::BlendFunc::BlendFuncMode> mBlendDest;
std::optional<osg::BlendEquation::Equation> mBlendEq;
};
}
#endif

View file

@ -0,0 +1,60 @@
#include "stateupdater.hpp"
#include <osg/BufferObject>
#include <osg/BufferIndexBinding>
#include <components/resource/scenemanager.hpp>
#include <components/debug/debuglog.hpp>
namespace fx
{
StateUpdater::StateUpdater(bool useUBO) : mUseUBO(useUBO) {}
void StateUpdater::setDefaults(osg::StateSet* stateset)
{
if (mUseUBO)
{
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
osg::ref_ptr<osg::BufferTemplate<UniformData::BufferType>> data = new osg::BufferTemplate<UniformData::BufferType>();
data->setBufferObject(ubo);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Resource::SceneManager::UBOBinding::PostProcessor), data, 0, mData.getGPUSize());
stateset->setAttributeAndModes(ubb, osg::StateAttribute::ON);
}
else
{
const auto createUniform = [&] (const auto& v) {
using T = std::decay_t<decltype(v)>;
std::string name = "omw." + std::string(T::sName);
stateset->addUniform(new osg::Uniform(name.c_str(), mData.get<T>()));
};
std::apply([&] (const auto& ... v) { (createUniform(v) , ...); }, mData.getData());
}
}
void StateUpdater::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
{
if (mUseUBO)
{
osg::UniformBufferBinding* ubb = dynamic_cast<osg::UniformBufferBinding*>(stateset->getAttribute(osg::StateAttribute::UNIFORMBUFFERBINDING, static_cast<int>(Resource::SceneManager::UBOBinding::PostProcessor)));
auto& dest = static_cast<osg::BufferTemplate<UniformData::BufferType>*>(ubb->getBufferData())->getData();
mData.copyTo(dest);
ubb->getBufferData()->dirty();
}
else
{
const auto setUniform = [&] (const auto& v) {
using T = std::decay_t<decltype(v)>;
std::string name = "omw." + std::string(T::sName);
stateset->getUniform(name)->set(mData.get<T>());
};
std::apply([&] (const auto& ... v) { (setUniform(v) , ...); }, mData.getData());
}
}
}

View file

@ -0,0 +1,192 @@
#ifndef OPENMW_COMPONENTS_FX_STATEUPDATER_H
#define OPENMW_COMPONENTS_FX_STATEUPDATER_H
#include <osg/BufferTemplate>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/std140/ubo.hpp>
namespace fx
{
class StateUpdater : public SceneUtil::StateSetUpdater
{
public:
StateUpdater(bool useUBO);
void setProjectionMatrix(const osg::Matrixf& matrix)
{
mData.get<ProjectionMatrix>() = matrix;
mData.get<InvProjectionMatrix>() = osg::Matrixf::inverse(matrix);
}
void setViewMatrix(const osg::Matrixf& matrix) { mData.get<ViewMatrix>() = matrix; }
void setInvViewMatrix(const osg::Matrixf& matrix) { mData.get<InvViewMatrix>() = matrix; }
void setPrevViewMatrix(const osg::Matrixf& matrix) { mData.get<PrevViewMatrix>() = matrix;}
void setEyePos(const osg::Vec3f& pos) { mData.get<EyePos>() = osg::Vec4f(pos, 0.f); }
void setEyeVec(const osg::Vec3f& vec) { mData.get<EyeVec>() = osg::Vec4f(vec, 0.f); }
void setFogColor(const osg::Vec4f& color) { mData.get<FogColor>() = color; }
void setSunColor(const osg::Vec4f& color) { mData.get<SunColor>() = color; }
void setSunPos(const osg::Vec4f& pos, bool night)
{
mData.get<SunPos>() = pos;
if (night)
mData.get<SunPos>().z() *= -1.f;
}
void setResolution(const osg::Vec2f& size)
{
mData.get<Resolution>() = size;
mData.get<RcpResolution>() = {1.f / size.x(), 1.f / size.y()};
}
void setSunVis(float vis)
{
mData.get<SunVis>() = vis;
}
void setFogRange(float near, float far)
{
mData.get<FogNear>() = near;
mData.get<FogFar>() = far;
}
void setNearFar(float near, float far)
{
mData.get<Near>() = near;
mData.get<Far>() = far;
}
void setIsUnderwater(bool underwater) { mData.get<IsUnderwater>() = underwater; }
void setIsInterior(bool interior) { mData.get<IsInterior>() = interior; }
void setFov(float fov) { mData.get<Fov>() = fov; }
void setGameHour(float hour) { mData.get<GameHour>() = hour; }
void setWeatherId(int id) { mData.get<WeatherID>() = id; }
void setNextWeatherId(int id) { mData.get<NextWeatherID>() = id; }
void setWaterHeight(float height) { mData.get<WaterHeight>() = height; }
void setSimulationTime(float time) { mData.get<SimulationTime>() = time; }
void setDeltaSimulationTime(float time) { mData.get<DeltaSimulationTime>() = time; }
void setWindSpeed(float speed) { mData.get<WindSpeed>() = speed; }
void setWeatherTransition(float transition) { mData.get<WeatherTransition>() = transition; }
static std::string getStructDefinition()
{
static std::string definition = UniformData::getDefinition("_omw_data");
return definition;
}
private:
struct ProjectionMatrix : std140::Mat4 { static constexpr std::string_view sName = "projectionMatrix"; };
struct InvProjectionMatrix : std140::Mat4 { static constexpr std::string_view sName = "invProjectionMatrix"; };
struct ViewMatrix : std140::Mat4 { static constexpr std::string_view sName = "viewMatrix"; };
struct PrevViewMatrix : std140::Mat4 { static constexpr std::string_view sName = "prevViewMatrix"; };
struct InvViewMatrix : std140::Mat4 { static constexpr std::string_view sName = "invViewMatrix"; };
struct EyePos : std140::Vec4 { static constexpr std::string_view sName = "eyePos"; };
struct EyeVec : std140::Vec4 { static constexpr std::string_view sName = "eyeVec"; };
struct FogColor : std140::Vec4 { static constexpr std::string_view sName = "fogColor"; };
struct SunColor : std140::Vec4 { static constexpr std::string_view sName = "sunColor"; };
struct SunPos : std140::Vec4 { static constexpr std::string_view sName = "sunPos"; };
struct Resolution : std140::Vec2 { static constexpr std::string_view sName = "resolution"; };
struct RcpResolution : std140::Vec2 { static constexpr std::string_view sName = "rcpResolution"; };
struct FogNear : std140::Float { static constexpr std::string_view sName = "fogNear"; };
struct FogFar : std140::Float { static constexpr std::string_view sName = "fogFar"; };
struct Near : std140::Float { static constexpr std::string_view sName = "near"; };
struct Far : std140::Float { static constexpr std::string_view sName = "far"; };
struct Fov : std140::Float { static constexpr std::string_view sName = "fov"; };
struct GameHour : std140::Float { static constexpr std::string_view sName = "gameHour"; };
struct SunVis : std140::Float { static constexpr std::string_view sName = "sunVis"; };
struct WaterHeight : std140::Float { static constexpr std::string_view sName = "waterHeight"; };
struct SimulationTime : std140::Float { static constexpr std::string_view sName = "simulationTime"; };
struct DeltaSimulationTime : std140::Float { static constexpr std::string_view sName = "deltaSimulationTime"; };
struct WindSpeed : std140::Float { static constexpr std::string_view sName = "windSpeed"; };
struct WeatherTransition : std140::Float { static constexpr std::string_view sName = "weatherTransition"; };
struct WeatherID : std140::Int { static constexpr std::string_view sName = "weatherID"; };
struct NextWeatherID : std140::Int { static constexpr std::string_view sName = "nextWeatherID"; };
struct IsUnderwater : std140::Bool { static constexpr std::string_view sName = "isUnderwater"; };
struct IsInterior : std140::Bool { static constexpr std::string_view sName = "isInterior"; };
using UniformData = std140::UBO<
ProjectionMatrix,
InvProjectionMatrix,
ViewMatrix,
PrevViewMatrix,
InvViewMatrix,
EyePos,
EyeVec,
FogColor,
SunColor,
SunPos,
Resolution,
RcpResolution,
FogNear,
FogFar,
Near,
Far,
Fov,
GameHour,
SunVis,
WaterHeight,
SimulationTime,
DeltaSimulationTime,
WindSpeed,
WeatherTransition,
WeatherID,
NextWeatherID,
IsUnderwater,
IsInterior
>;
private:
void setDefaults(osg::StateSet* stateset) override;
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override;
UniformData mData;
bool mUseUBO;
};
}
#endif

1049
components/fx/technique.cpp Normal file

File diff suppressed because it is too large Load diff

300
components/fx/technique.hpp Normal file
View file

@ -0,0 +1,300 @@
#ifndef OPENMW_COMPONENTS_FX_TECHNIQUE_H
#define OPENMW_COMPONENTS_FX_TECHNIQUE_H
#include <vector>
#include <string>
#include <variant>
#include <memory>
#include <unordered_map>
#include <filesystem>
#include <osg/Node>
#include <osg/Program>
#include <osg/Shader>
#include <osg/Texture2D>
#include <osg/StateSet>
#include <osg/FrameBufferObject>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/Vec4f>
#include <osg/BlendFunc>
#include <osg/BlendEquation>
#include "pass.hpp"
#include "lexer.hpp"
#include "types.hpp"
namespace Resource
{
class ImageManager;
}
namespace VFS
{
class Manager;
}
namespace fx
{
using FlagsType = size_t;
struct DispatchNode
{
DispatchNode() = default;
DispatchNode(const DispatchNode& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: mHandle(other.mHandle)
, mFlags(other.mFlags)
, mRootStateSet(other.mRootStateSet)
{
mPasses.reserve(other.mPasses.size());
for (const auto& subpass : other.mPasses)
mPasses.emplace_back(subpass, copyOp);
}
struct SubPass {
SubPass() = default;
osg::ref_ptr<osg::StateSet> mStateSet = new osg::StateSet;
osg::ref_ptr<osg::FrameBufferObject> mRenderTarget;
osg::ref_ptr<osg::Texture2D> mRenderTexture;
SubPass(const SubPass& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: mStateSet(new osg::StateSet(*other.mStateSet, copyOp))
{
if (other.mRenderTarget)
mRenderTarget = new osg::FrameBufferObject(*other.mRenderTarget, copyOp);
if (other.mRenderTexture)
mRenderTexture = new osg::Texture2D(*other.mRenderTexture, copyOp);
}
};
// not safe to read/write in draw thread
std::shared_ptr<fx::Technique> mHandle = nullptr;
FlagsType mFlags = 0;
std::vector<SubPass> mPasses;
osg::ref_ptr<osg::StateSet> mRootStateSet = new osg::StateSet;
};
using DispatchArray = std::vector<DispatchNode>;
class Technique
{
public:
using PassList = std::vector<std::shared_ptr<Pass>>;
using TexList = std::vector<osg::ref_ptr<osg::Texture>>;
using UniformMap = std::vector<std::shared_ptr<Types::UniformBase>>;
using RenderTargetMap = std::unordered_map<std::string_view, Types::RenderTarget>;
inline static std::string sExt = ".omwfx";
inline static std::string sSubdir = "shaders";
enum class Status
{
Success,
Uncompiled,
File_Not_exists,
Parse_Error
};
static constexpr FlagsType Flag_Disable_Interiors = (1 << 0);
static constexpr FlagsType Flag_Disable_Exteriors = (1 << 1);
static constexpr FlagsType Flag_Disable_Underwater = (1 << 2);
static constexpr FlagsType Flag_Disable_Abovewater = (1 << 3);
static constexpr FlagsType Flag_Disable_SunGlare = (1 << 4);
static constexpr FlagsType Flag_Hidden = (1 << 5);
Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, const std::string& name, int width, int height, bool ubo, bool supportsNormals);
bool compile();
std::string getName() const;
std::string getFileName() const;
void setLastModificationTime(std::filesystem::file_time_type timeStamp, bool dirty = true);
bool isDirty() const { return mDirty; }
void setDirty(bool dirty) { mDirty = dirty; }
bool isValid() const { return mValid; }
bool getHDR() const { return mHDR; }
bool getNormals() const { return mNormals && mSupportsNormals; }
const PassList& getPasses() { return mPasses; }
const TexList& getTextures() const { return mTextures; }
Status getStatus() const { return mStatus; }
std::string_view getAuthor() const { return mAuthor; }
std::string_view getDescription() const { return mDescription; }
std::string_view getVersion() const { return mVersion; }
int getGLSLVersion() const { return mGLSLVersion; }
std::string getGLSLProfile() const { return mGLSLProfile; }
const std::unordered_set<std::string>& getGLSLExtensions() const { return mGLSLExtensions; }
osg::ref_ptr<osg::Texture2D> getMainTemplate() const { return mMainTemplate; }
FlagsType getFlags() const { return mFlags; }
bool getHidden() const { return mFlags & Flag_Hidden; }
UniformMap& getUniformMap() { return mDefinedUniforms; }
RenderTargetMap& getRenderTargetsMap() { return mRenderTargets; }
std::string getLastError() const { return mLastError; }
UniformMap::iterator findUniform(const std::string& name);
private:
[[noreturn]] void error(const std::string& msg);
void clear();
std::string_view asLiteral() const;
template<class T>
void expect(const std::string& err="");
template<class T, class T2>
void expect(const std::string& err="");
template <class T>
bool isNext();
void parse(std::string&& buffer);
template <class SrcT, class T>
void parseUniform();
template <class T>
void parseSampler();
template <class T>
void parseBlock(bool named=true);
template <class T>
void parseBlockImp() {}
void parseBlockHeader();
bool parseBool();
std::string_view parseString();
float parseFloat();
int parseInteger();
int parseInternalFormat();
int parseSourceType();
int parseSourceFormat();
osg::BlendEquation::Equation parseBlendEquation();
osg::BlendFunc::BlendFuncMode parseBlendFuncMode();
osg::Texture::WrapMode parseWrapMode();
osg::Texture::InternalFormatMode parseCompression();
FlagsType parseFlags();
osg::Texture::FilterMode parseFilterMode();
template <class TDelimeter>
std::vector<std::string_view> parseLiteralList();
template <class OSGVec, class T>
OSGVec parseVec();
std::string getBlockWithLineDirective();
std::unique_ptr<Lexer::Lexer> mLexer;
Lexer::Token mToken;
std::string mShared;
std::string mName;
std::string mFileName;
std::string_view mBlockName;
std::string_view mAuthor;
std::string_view mDescription;
std::string_view mVersion;
std::unordered_set<std::string> mGLSLExtensions;
int mGLSLVersion;
std::string mGLSLProfile;
FlagsType mFlags;
Status mStatus;
bool mEnabled;
std::filesystem::file_time_type mLastModificationTime;
bool mDirty;
bool mValid;
bool mHDR;
bool mNormals;
int mWidth;
int mHeight;
osg::ref_ptr<osg::Texture2D> mMainTemplate;
RenderTargetMap mRenderTargets;
TexList mTextures;
PassList mPasses;
std::unordered_map<std::string_view, std::shared_ptr<Pass>> mPassMap;
std::vector<std::string_view> mPassKeys;
Pass::Type mLastAppliedType;
UniformMap mDefinedUniforms;
const VFS::Manager& mVFS;
Resource::ImageManager& mImageManager;
bool mUBO;
bool mSupportsNormals;
std::string mBuffer;
std::string mLastError;
};
template<> void Technique::parseBlockImp<Lexer::Shared>();
template<> void Technique::parseBlockImp<Lexer::Technique>();
template<> void Technique::parseBlockImp<Lexer::Main_Pass>();
template<> void Technique::parseBlockImp<Lexer::Render_Target>();
template<> void Technique::parseBlockImp<Lexer::Vertex>();
template<> void Technique::parseBlockImp<Lexer::Fragment>();
template<> void Technique::parseBlockImp<Lexer::Compute>();
template<> void Technique::parseBlockImp<Lexer::Sampler_1D>();
template<> void Technique::parseBlockImp<Lexer::Sampler_2D>();
template<> void Technique::parseBlockImp<Lexer::Sampler_3D>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Bool>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Float>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Int>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Vec2>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Vec3>();
template<> void Technique::parseBlockImp<Lexer::Uniform_Vec4>();
}
#endif

259
components/fx/types.hpp Normal file
View file

@ -0,0 +1,259 @@
#ifndef OPENMW_COMPONENTS_FX_TYPES_H
#define OPENMW_COMPONENTS_FX_TYPES_H
#include <optional>
#include <osg/Camera>
#include <osg/Uniform>
#include <osg/Texture2D>
#include <osg/FrameBufferObject>
#include <osg/BlendFunc>
#include <osg/BlendEquation>
#include <MyGUI_Widget.h>
#include <components/sceneutil/depth.hpp>
#include <components/settings/shadermanager.hpp>
#include <components/misc/stringops.hpp>
#include <components/debug/debuglog.hpp>
#include "pass.hpp"
namespace fx
{
namespace Types
{
struct SizeProxy
{
std::optional<float> mWidthRatio;
std::optional<float> mHeightRatio;
std::optional<int> mWidth;
std::optional<int> mHeight;
std::tuple<int,int> get(int width, int height) const
{
int scaledWidth = width;
int scaledHeight = height;
if (mWidthRatio)
scaledWidth = width * mWidthRatio.value();
else if (mWidth)
scaledWidth = mWidth.value();
if (mHeightRatio > 0.f)
scaledHeight = height * mHeightRatio.value();
else if (mHeight)
scaledHeight = mHeight.value();
return std::make_tuple(scaledWidth, scaledHeight);
}
};
struct RenderTarget
{
osg::ref_ptr<osg::Texture2D> mTarget = new osg::Texture2D;
SizeProxy mSize;
bool mMipMap = false;
};
template <class T>
struct Uniform
{
std::optional<T> mValue;
T mDefault;
T mMin = std::numeric_limits<T>::lowest();
T mMax = std::numeric_limits<T>::max();
using value_type = T;
T getValue() const
{
return mValue.value_or(mDefault);
}
};
using Uniform_t = std::variant<
Uniform<osg::Vec2f>,
Uniform<osg::Vec3f>,
Uniform<osg::Vec4f>,
Uniform<bool>,
Uniform<float>,
Uniform<int>
>;
enum SamplerType
{
Texture_1D,
Texture_2D,
Texture_3D
};
struct UniformBase
{
std::string mName;
std::string mHeader;
std::string mTechniqueName;
std::string mDescription;
bool mStatic = true;
std::optional<SamplerType> mSamplerType = std::nullopt;
double mStep;
Uniform_t mData;
template <class T>
T getValue() const
{
auto value = Settings::ShaderManager::get().getValue<T>(mTechniqueName, mName);
return value.value_or(std::get<Uniform<T>>(mData).getValue());
}
template <class T>
T getMin() const
{
return std::get<Uniform<T>>(mData).mMin;
}
template <class T>
T getMax() const
{
return std::get<Uniform<T>>(mData).mMax;
}
template <class T>
T getDefault() const
{
return std::get<Uniform<T>>(mData).mDefault;
}
template <class T>
void setValue(const T& value)
{
std::visit([&, value](auto&& arg){
using U = typename std::decay_t<decltype(arg)>::value_type;
if constexpr (std::is_same_v<T, U>)
{
arg.mValue = value;
if (mStatic)
Settings::ShaderManager::get().setValue<T>(mTechniqueName, mName, value);
}
else
{
Log(Debug::Warning) << "Attempting to set uniform '" << mName << "' with wrong type";
}
}, mData);
}
void setUniform(osg::Uniform* uniform)
{
auto type = getType();
if (!type || type.value() != uniform->getType())
return;
std::visit([&](auto&& arg)
{
const auto value = arg.getValue();
uniform->set(value);
}, mData);
}
std::optional<osg::Uniform::Type> getType() const
{
return std::visit([](auto&& arg) -> std::optional<osg::Uniform::Type> {
using T = typename std::decay_t<decltype(arg)>::value_type;
if constexpr (std::is_same_v<T, osg::Vec2f>)
return osg::Uniform::FLOAT_VEC2;
else if constexpr (std::is_same_v<T, osg::Vec3f>)
return osg::Uniform::FLOAT_VEC3;
else if constexpr (std::is_same_v<T, osg::Vec4f>)
return osg::Uniform::FLOAT_VEC4;
else if constexpr (std::is_same_v<T, float>)
return osg::Uniform::FLOAT;
else if constexpr (std::is_same_v<T, int>)
return osg::Uniform::INT;
else if constexpr (std::is_same_v<T, bool>)
return osg::Uniform::BOOL;
return std::nullopt;
}, mData);
}
std::optional<std::string> getGLSL()
{
if (mSamplerType)
{
switch (mSamplerType.value())
{
case Texture_1D:
return Misc::StringUtils::format("uniform sampler1D %s;", mName);
case Texture_2D:
return Misc::StringUtils::format("uniform sampler2D %s;", mName);
case Texture_3D:
return Misc::StringUtils::format("uniform sampler3D %s;", mName);
}
}
bool useUniform = (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug || mStatic == false);
return std::visit([&](auto&& arg) -> std::optional<std::string> {
using T = typename std::decay_t<decltype(arg)>::value_type;
auto value = arg.getValue();
if constexpr (std::is_same_v<T, osg::Vec2f>)
{
if (useUniform)
return Misc::StringUtils::format("uniform vec2 %s;", mName);
return Misc::StringUtils::format("const vec2 %s=vec2(%f,%f);", mName, value[0], value[1]);
}
else if constexpr (std::is_same_v<T, osg::Vec3f>)
{
if (useUniform)
return Misc::StringUtils::format("uniform vec3 %s;", mName);
return Misc::StringUtils::format("const vec3 %s=vec3(%f,%f,%f);", mName, value[0], value[1], value[2]);
}
else if constexpr (std::is_same_v<T, osg::Vec4f>)
{
if (useUniform)
return Misc::StringUtils::format("uniform vec4 %s;", mName);
return Misc::StringUtils::format("const vec4 %s=vec4(%f,%f,%f,%f);", mName, value[0], value[1], value[2], value[3]);
}
else if constexpr (std::is_same_v<T, float>)
{
if (useUniform)
return Misc::StringUtils::format("uniform float %s;", mName);
return Misc::StringUtils::format("const float %s=%f;", mName, value);
}
else if constexpr (std::is_same_v<T, int>)
{
if (useUniform)
return Misc::StringUtils::format("uniform int %s;", mName);
return Misc::StringUtils::format("const int %s=%i;", mName, value);
}
else if constexpr (std::is_same_v<T, bool>)
{
if (useUniform)
return Misc::StringUtils::format("uniform bool %s;", mName);
return Misc::StringUtils::format("const bool %s=%s;", mName, value ? "true" : "false");
}
return std::nullopt;
}, mData);
}
};
}
}
#endif

164
components/fx/widgets.cpp Normal file
View file

@ -0,0 +1,164 @@
#include "widgets.hpp"
#include <components/widgets/box.hpp>
namespace
{
template <class T, class WidgetT>
void createVectorWidget(const std::shared_ptr<fx::Types::UniformBase>& uniform, MyGUI::Widget* client, fx::Widgets::UniformBase* base)
{
int height = client->getHeight();
base->setSize(base->getSize().width, (base->getSize().height - height) + (height * T::num_components));
client->setSize(client->getSize().width, height * T::num_components);
for (int i = 0; i < T::num_components; ++i)
{
auto* widget = client->createWidget<WidgetT>("MW_ValueEditNumber", {0, height * i, client->getWidth(), height}, MyGUI::Align::Default);
widget->setData(uniform, static_cast<fx::Widgets::Index>(i));
base->addItem(widget);
}
}
}
namespace fx
{
namespace Widgets
{
void EditBool::setValue(bool value)
{
auto uniform = mUniform.lock();
if (!uniform)
return;
mCheckbutton->setCaptionWithReplacing(value ? "#{sOn}" : "#{sOff}");
uniform->setValue<bool>(value);
}
void EditBool::setValueFromUniform()
{
auto uniform = mUniform.lock();
if (!uniform)
return;
setValue(uniform->template getValue<bool>());
}
void EditBool::toDefault()
{
auto uniform = mUniform.lock();
if (!uniform)
return;
setValue(uniform->getDefault<bool>());
}
void EditBool::initialiseOverride()
{
Base::initialiseOverride();
assignWidget(mCheckbutton, "Checkbutton");
mCheckbutton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditBool::notifyMouseButtonClick);
}
void EditBool::notifyMouseButtonClick(MyGUI::Widget* sender)
{
auto uniform = mUniform.lock();
if (!uniform)
return;
setValue(!uniform->getValue<bool>());
}
void UniformBase::init(const std::shared_ptr<fx::Types::UniformBase>& uniform)
{
mLabel->setCaption(uniform->mName);
if (uniform->mDescription.empty())
{
mLabel->setUserString("ToolTipType", "");
}
else
{
mLabel->setUserString("ToolTipType", "Layout");
mLabel->setUserString("ToolTipLayout", "TextToolTip");
mLabel->setUserString("Caption_Text", uniform->mDescription);
}
std::visit([this, &uniform](auto&& arg) {
using T = typename std::decay_t<decltype(arg)>::value_type;
if constexpr (std::is_same_v<osg::Vec4f, T>)
{
createVectorWidget<T, EditNumberFloat4>(uniform, mClient, this);
}
else if constexpr (std::is_same_v<osg::Vec3f, T>)
{
createVectorWidget<T, EditNumberFloat3>(uniform, mClient, this);
}
else if constexpr (std::is_same_v<osg::Vec2f, T>)
{
createVectorWidget<T, EditNumberFloat2>(uniform, mClient, this);
}
else if constexpr (std::is_same_v<T, float>)
{
auto* widget = mClient->createWidget<EditNumberFloat>("MW_ValueEditNumber", {0, 0, mClient->getWidth(), mClient->getHeight()}, MyGUI::Align::Stretch);
widget->setData(uniform);
mBases.emplace_back(widget);
}
else if constexpr (std::is_same_v<T, int>)
{
auto* widget = mClient->createWidget<EditNumberInt>("MW_ValueEditNumber", {0, 0, mClient->getWidth(), mClient->getHeight()}, MyGUI::Align::Stretch);
widget->setData(uniform);
mBases.emplace_back(widget);
}
else if constexpr (std::is_same_v<T, bool>)
{
auto* widget = mClient->createWidget<EditBool>("MW_ValueEditBool", {0, 0, mClient->getWidth(), mClient->getHeight()}, MyGUI::Align::Stretch);
widget->setData(uniform);
mBases.emplace_back(widget);
}
mReset->eventMouseButtonClick += MyGUI::newDelegate(this, &UniformBase::notifyResetClicked);
for (EditBase* base : mBases)
base->setValueFromUniform();
}, uniform->mData);
}
void UniformBase::addItem(EditBase* item)
{
mBases.emplace_back(item);
}
void UniformBase::toDefault()
{
for (EditBase* base : mBases)
{
if (base)
base->toDefault();
}
}
void UniformBase::notifyResetClicked(MyGUI::Widget* sender)
{
toDefault();
}
void UniformBase::initialiseOverride()
{
Base::initialiseOverride();
assignWidget(mReset, "Reset");
assignWidget(mLabel, "Label");
assignWidget(mClient, "Client");
}
}
}

266
components/fx/widgets.hpp Normal file
View file

@ -0,0 +1,266 @@
#ifndef OPENMW_COMPONENTS_FX_WIDGETS_H
#define OPENMW_COMPONENTS_FX_WIDGETS_H
#include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_InputManager.h>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/Vec4f>
#include <components/misc/stringops.hpp>
#include "technique.hpp"
#include "types.hpp"
namespace Gui
{
class AutoSizedTextBox;
class AutoSizedButton;
}
namespace fx
{
namespace Widgets
{
enum Index
{
None = -1,
Zero = 0,
One = 1,
Two = 2,
Three = 3
};
class EditBase
{
public:
virtual ~EditBase() = default;
void setData(const std::shared_ptr<fx::Types::UniformBase>& uniform, Index index = None)
{
mUniform = uniform;
mIndex = index;
}
virtual void setValueFromUniform() = 0;
virtual void toDefault() = 0;
protected:
std::weak_ptr<fx::Types::UniformBase> mUniform;
Index mIndex;
};
class EditBool : public EditBase, public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(EditBool)
public:
void setValue(bool value);
void setValueFromUniform() override;
void toDefault() override;
private:
void initialiseOverride() override;
void notifyMouseButtonClick(MyGUI::Widget* sender);
MyGUI::Button* mCheckbutton;
};
template <class T, class UType>
class EditNumber : public EditBase, public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(EditNumber)
public:
EditNumber() : mLastPointerX(0) {}
void setValue(T value)
{
mValue = value;
if constexpr (std::is_floating_point_v<T>)
mValueLabel->setCaption(Misc::StringUtils::format("%.3f", mValue));
else
mValueLabel->setCaption(std::to_string(mValue));
if (auto uniform = mUniform.lock())
{
if constexpr (std::is_fundamental_v<UType>)
uniform->template setValue<UType>(mValue);
else
{
UType uvalue = uniform->template getValue<UType>();
uvalue[mIndex] = mValue;
uniform->template setValue<UType>(uvalue);
}
}
}
void setValueFromUniform() override
{
if (auto uniform = mUniform.lock())
{
T value;
if constexpr (std::is_fundamental_v<UType>)
value = uniform->template getValue<UType>();
else
value = uniform->template getValue<UType>()[mIndex];
setValue(value);
}
}
void toDefault() override
{
if (auto uniform = mUniform.lock())
{
if constexpr (std::is_fundamental_v<UType>)
setValue(uniform->template getDefault<UType>());
else
setValue(uniform->template getDefault<UType>()[mIndex]);
}
}
private:
void initialiseOverride() override
{
Base::initialiseOverride();
assignWidget(mDragger, "Dragger");
assignWidget(mValueLabel, "Value");
assignWidget(mButtonIncrease, "ButtonIncrease");
assignWidget(mButtonDecrease, "ButtonDecrease");
mButtonIncrease->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNumber::notifyButtonClicked);
mButtonDecrease->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNumber::notifyButtonClicked);
mDragger->eventMouseButtonPressed += MyGUI::newDelegate(this, &EditNumber::notifyMouseButtonPressed);
mDragger->eventMouseDrag += MyGUI::newDelegate(this, &EditNumber::notifyMouseButtonDragged);
mDragger->eventMouseWheel += MyGUI::newDelegate(this, &EditNumber::notifyMouseWheel);
}
void notifyMouseWheel(MyGUI::Widget* sender, int rel)
{
auto uniform = mUniform.lock();
if (!uniform)
return;
if (rel > 0)
increment(uniform->mStep);
else
increment(-uniform->mStep);
}
void notifyMouseButtonDragged(MyGUI::Widget* sender, int left, int top, MyGUI::MouseButton id)
{
if (id != MyGUI::MouseButton::Left)
return;
auto uniform = mUniform.lock();
if (!uniform)
return;
int delta = left - mLastPointerX;
// allow finer tuning when shift is pressed
constexpr double scaling = 20.0;
T step = MyGUI::InputManager::getInstance().isShiftPressed() ? uniform->mStep / scaling : uniform->mStep;
if (step == 0)
{
if constexpr (std::is_integral_v<T>)
step = 1;
else
step = uniform->mStep;
}
if (delta > 0)
increment(step);
else if (delta < 0)
increment(-step);
mLastPointerX = left;
}
void notifyMouseButtonPressed(MyGUI::Widget* sender, int left, int top, MyGUI::MouseButton id)
{
if (id != MyGUI::MouseButton::Left)
return;
mLastPointerX = left;
}
void increment(T step)
{
auto uniform = mUniform.lock();
if (!uniform)
return;
if constexpr (std::is_fundamental_v<UType>)
setValue(std::clamp<T>(uniform->template getValue<UType>() + step, uniform->template getMin<UType>(), uniform->template getMax<T>()));
else
setValue(std::clamp<T>(uniform->template getValue<UType>()[mIndex] + step, uniform->template getMin<UType>()[mIndex], uniform->template getMax<UType>()[mIndex]));
}
void notifyButtonClicked(MyGUI::Widget* sender)
{
auto uniform = mUniform.lock();
if (!uniform)
return;
if (sender == mButtonDecrease)
increment(-uniform->mStep);
else if (sender == mButtonIncrease)
increment(uniform->mStep);
}
MyGUI::Button* mButtonDecrease;
MyGUI::Button* mButtonIncrease;
MyGUI::Widget* mDragger;
MyGUI::TextBox* mValueLabel;
T mValue;
int mLastPointerX;
};
class EditNumberFloat4 : public EditNumber<float, osg::Vec4f> { MYGUI_RTTI_DERIVED(EditNumberFloat4) };
class EditNumberFloat3 : public EditNumber<float, osg::Vec3f> { MYGUI_RTTI_DERIVED(EditNumberFloat3) };
class EditNumberFloat2 : public EditNumber<float, osg::Vec2f> { MYGUI_RTTI_DERIVED(EditNumberFloat2) };
class EditNumberFloat : public EditNumber<float, float> { MYGUI_RTTI_DERIVED(EditNumberFloat) };
class EditNumberInt : public EditNumber<int, int> { MYGUI_RTTI_DERIVED(EditNumberInt) };
class UniformBase final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(UniformBase)
public:
void init(const std::shared_ptr<fx::Types::UniformBase>& uniform);
void toDefault();
void addItem(EditBase* item);
private:
void notifyResetClicked(MyGUI::Widget* sender);
void initialiseOverride() override;
Gui::AutoSizedButton* mReset;
Gui::AutoSizedTextBox* mLabel;
MyGUI::Widget* mClient;
std::vector<EditBase*> mBases;
};
}
}
#endif

View file

@ -318,7 +318,7 @@ namespace Resource
, mApplyLightingToEnvMaps(false)
, mLightingMethod(SceneUtil::LightingMethod::FFP)
, mConvertAlphaTestToAlphaToCoverage(false)
, mDepthFormat(0)
, mSupportsNormalsRT(false)
, mSharedStateManager(new SharedStateManager)
, mImageManager(imageManager)
, mNifFileManager(nifFileManager)
@ -348,7 +348,7 @@ namespace Resource
if (forceShadersForNode)
shaderVisitor->setForceShaders(true);
if (disableSoftParticles)
shaderVisitor->setOpaqueDepthTex(nullptr);
shaderVisitor->setOpaqueDepthTex(nullptr, nullptr);
node->accept(*shaderVisitor);
}
@ -368,16 +368,6 @@ namespace Resource
return mClampLighting;
}
void SceneManager::setDepthFormat(GLenum format)
{
mDepthFormat = format;
}
GLenum SceneManager::getDepthFormat() const
{
return mDepthFormat;
}
void SceneManager::setAutoUseNormalMaps(bool use)
{
mAutoUseNormalMaps = use;
@ -440,9 +430,9 @@ namespace Resource
mConvertAlphaTestToAlphaToCoverage = convert;
}
void SceneManager::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture)
void SceneManager::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texturePing, osg::ref_ptr<osg::Texture2D> texturePong)
{
mOpaqueDepthTex = texture;
mOpaqueDepthTex = { texturePing, texturePong };
}
SceneManager::~SceneManager()
@ -930,7 +920,8 @@ namespace Resource
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex);
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex[0], mOpaqueDepthTex[1]);
shaderVisitor->setSupportsNormalsRT(mSupportsNormalsRT);
return shaderVisitor;
}
}

View file

@ -91,9 +91,6 @@ namespace Resource
void setClampLighting(bool clamp);
bool getClampLighting() const;
void setDepthFormat(GLenum format);
GLenum getDepthFormat() const;
/// @see ShaderVisitor::setAutoUseNormalMaps
void setAutoUseNormalMaps(bool use);
@ -112,12 +109,13 @@ namespace Resource
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture);
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texturePing, osg::ref_ptr<osg::Texture2D> texturePong);
enum class UBOBinding
{
// If we add more UBO's, we should probably assign their bindings dynamically according to the current count of UBO's in the programTemplate
LightBuffer
LightBuffer,
PostProcessor
};
void setLightingMethod(SceneUtil::LightingMethod method);
SceneUtil::LightingMethod getLightingMethod() const;
@ -195,6 +193,9 @@ namespace Resource
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
void setSupportsNormalsRT(bool supports) { mSupportsNormalsRT = supports; }
bool getSupportsNormalsRT() const { return mSupportsNormalsRT; }
private:
Shader::ShaderVisitor* createShaderVisitor(const std::string& shaderPrefix = "objects");
@ -211,8 +212,8 @@ namespace Resource
SceneUtil::LightingMethod mLightingMethod;
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
bool mConvertAlphaTestToAlphaToCoverage;
GLenum mDepthFormat;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
bool mSupportsNormalsRT;
std::array<osg::ref_ptr<osg::Texture2D>, 2> mOpaqueDepthTex;
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
mutable std::mutex mSharedStateMutex;

View file

@ -0,0 +1,42 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_CLEARCOLOR_H
#define OPENMW_COMPONENTS_SCENEUTIL_CLEARCOLOR_H
#include <osg/StateAttribute>
#include <osg/Vec4f>
namespace SceneUtil
{
class ClearColor : public osg::StateAttribute
{
public:
ClearColor() : mMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) {}
ClearColor(const osg::Vec4f& color, GLbitfield mask) : mColor(color), mMask(mask) {}
ClearColor(const ClearColor& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
: osg::StateAttribute(copy,copyop), mColor(copy.mColor), mMask(copy.mMask) {}
META_StateAttribute(fx, ClearColor, static_cast<osg::StateAttribute::Type>(100))
int compare(const StateAttribute& sa) const override
{
COMPARE_StateAttribute_Types(ClearColor, sa);
COMPARE_StateAttribute_Parameter(mColor);
COMPARE_StateAttribute_Parameter(mMask);
return 0;
}
void apply(osg::State& state) const override
{
glClearColor(mColor[0], mColor[1], mColor[2], mColor[3]);
glClear(mMask);
}
private:
osg::Vec4f mColor;
GLbitfield mMask;
};
}
#endif

View file

@ -44,18 +44,6 @@ namespace SceneUtil
);
}
bool isFloatingPointDepthFormat(GLenum format)
{
constexpr std::array<GLenum, 4> formats = {
GL_DEPTH_COMPONENT32F,
GL_DEPTH_COMPONENT32F_NV,
GL_DEPTH32F_STENCIL8,
GL_DEPTH32F_STENCIL8_NV,
};
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
}
bool isDepthFormat(GLenum format)
{
constexpr std::array<GLenum, 8> formats = {

View file

@ -45,9 +45,6 @@ namespace SceneUtil
// Returns an orthographic projection matrix for use with a reversed z-buffer.
osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, double bottom, double top, double near, double far);
// Returns true if the GL format is a floating point depth format.
bool isFloatingPointDepthFormat(GLenum format);
// Returns true if the GL format is a depth format
bool isDepthFormat(GLenum format);

View file

@ -33,7 +33,7 @@ namespace SceneUtil
, mSamples(samples)
, mGenerateMipmaps(generateMipmaps)
, mColorBufferInternalFormat(Color::colorInternalFormat())
, mDepthBufferInternalFormat(AutoDepth::depthInternalFormat())
, mDepthBufferInternalFormat(SceneUtil::AutoDepth::depthInternalFormat())
, mRenderOrderNum(renderOrderNum)
, mStereoAwareness(stereoAwareness)
{

View file

@ -152,42 +152,6 @@ void GlowUpdater::setDuration(float duration)
mDuration = duration;
}
// Allows camera to render to a color and floating point depth texture with a multisampled framebuffer.
class AttachMultisampledDepthColorCallback : public SceneUtil::NodeCallback<AttachMultisampledDepthColorCallback, osg::Node*, osgUtil::CullVisitor*>
{
public:
AttachMultisampledDepthColorCallback(osg::Texture2D* colorTex, osg::Texture2D* depthTex, int samples, int colorSamples)
{
int width = colorTex->getTextureWidth();
int height = colorTex->getTextureHeight();
osg::ref_ptr<osg::RenderBuffer> rbColor = new osg::RenderBuffer(width, height, colorTex->getInternalFormat(), samples, colorSamples);
osg::ref_ptr<osg::RenderBuffer> rbDepth = new osg::RenderBuffer(width, height, depthTex->getInternalFormat(), samples, colorSamples);
mMsaaFbo = new osg::FrameBufferObject;
mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(rbColor));
mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(rbDepth));
mFbo = new osg::FrameBufferObject;
mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorTex));
mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthTex));
}
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
{
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
renderStage->setMultisampleResolveFramebufferObject(mFbo);
renderStage->setFrameBufferObject(mMsaaFbo);
traverse(node, cv);
}
private:
osg::ref_ptr<osg::FrameBufferObject> mFbo;
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
};
osg::Vec4f colourFromRGB(unsigned int clr)
{
osg::Vec4f colour(((clr >> 0) & 0xFF) / 255.0f,

View file

@ -0,0 +1,64 @@
#ifndef OPENMW_COMPONENTS_SERIALIZATION_OSGYAML_H
#define OPENMW_COMPONENTS_SERIALIZATION_OSGYAML_H
#include <yaml-cpp/yaml.h>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/Vec4f>
namespace Serialization
{
template <class OSGVec>
YAML::Node encodeOSGVec(const OSGVec& rhs)
{
YAML::Node node;
for (int i = 0; i < OSGVec::num_components; ++i)
node.push_back(rhs[i]);
return node;
}
template <class OSGVec>
bool decodeOSGVec(const YAML::Node& node, OSGVec& rhs)
{
if (!node.IsSequence() || node.size() != OSGVec::num_components)
return false;
for (int i = 0; i < OSGVec::num_components; ++i)
rhs[i] = node[i].as<typename OSGVec::value_type>();
return true;
}
}
namespace YAML
{
template<>
struct convert<osg::Vec2f>
{
static Node encode(const osg::Vec2f& rhs) { return Serialization::encodeOSGVec(rhs); }
static bool decode(const Node& node, osg::Vec2f& rhs) { return Serialization::decodeOSGVec(node, rhs); }
};
template<>
struct convert<osg::Vec3f>
{
static Node encode(const osg::Vec3f& rhs) { return Serialization::encodeOSGVec(rhs); }
static bool decode(const Node& node, osg::Vec3f& rhs) { return Serialization::decodeOSGVec(node, rhs); }
};
template<>
struct convert<osg::Vec4f>
{
static Node encode(const osg::Vec4f& rhs) { return Serialization::encodeOSGVec(rhs); }
static bool decode(const Node& node, osg::Vec4f& rhs) { return Serialization::decodeOSGVec(node, rhs); }
};
}
#endif

View file

@ -0,0 +1,174 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_SHADERMANAGER_H
#define OPENMW_COMPONENTS_SETTINGS_SHADERMANAGER_H
#include <unordered_map>
#include <filesystem>
#include <optional>
#include <fstream>
#include <yaml-cpp/yaml.h>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/Vec4f>
#include <components/serialization/osgyaml.hpp>
#include <components/debug/debuglog.hpp>
namespace Settings
{
/*
* Manages the shader.yaml file which is auto-generated and lives next to settings.cfg.
* This YAML file is simply a mapping of technique name to a list of uniforms and their values.
* Currently only vec2f, vec3f, vec4f, int, and float uniforms are supported.
*
* config:
* TECHNIQUE:
* MY_FLOAT: 10.34
* MY_VEC2: [0.23, 0.34]
* TECHNIQUE2:
* MY_VEC3: [0.22, 0.33, 0.20]
*/
class ShaderManager
{
public:
enum class Mode
{
Normal,
Debug
};
ShaderManager() = default;
ShaderManager(ShaderManager const&) = delete;
void operator=(ShaderManager const&) = delete;
static ShaderManager& get()
{
static ShaderManager instance;
return instance;
}
Mode getMode()
{
return mMode;
}
void setMode(Mode mode)
{
mMode = mode;
}
const YAML::Node& getRoot()
{
return mData;
}
template <class T>
bool setValue(const std::string& tname, const std::string& uname, const T& value)
{
if (mData.IsNull())
{
Log(Debug::Warning) << "Failed setting " << tname << ", " << uname << " : shader settings failed to load";
return false;
}
mData["config"][tname][uname] = value;
return true;
}
template <class T>
std::optional<T> getValue(const std::string& tname, const std::string& uname)
{
if (mData.IsNull())
{
Log(Debug::Warning) << "Failed getting " << tname << ", " << uname << " : shader settings failed to load";
return std::nullopt;
}
try
{
auto value = mData["config"][tname][uname];
if (!value)
return std::nullopt;
return value.as<T>();
}
catch(const YAML::BadConversion& e)
{
Log(Debug::Warning) << "Failed retrieving " << tname << ", " << uname << " : mismatched types in config file.";
}
return std::nullopt;
}
bool load(const std::string& path)
{
mData = YAML::Null;
mPath = std::filesystem::path(path);
Log(Debug::Info) << "Loading shader settings file: " << mPath;
if (!std::filesystem::exists(mPath))
{
std::ofstream fout(mPath);
if (!fout)
{
Log(Debug::Error) << "Failed creating shader settings file: " << mPath;
return false;
}
}
try
{
mData = YAML::LoadFile(mPath.string());
mData.SetStyle(YAML::EmitterStyle::Block);
if (!mData["config"])
mData["config"] = YAML::Node();
return true;
}
catch(const YAML::Exception& e)
{
Log(Debug::Error) << "Shader settings failed to load, " << e.msg;
}
return false;
}
bool save()
{
if (mData.IsNull())
{
Log(Debug::Error) << "Shader settings failed to load, settings will not be saved: " << mPath;
return false;
}
Log(Debug::Info) << "Saving shader settings file: " << mPath;
YAML::Emitter out;
out.SetMapFormat(YAML::Block);
out << mData;
std::ofstream fout(mPath.string());
fout << out.c_str();
if (!fout)
{
Log(Debug::Error) << "Failed saving shader settings file: " << mPath;
return false;
}
return true;
}
private:
std::filesystem::path mPath;
YAML::Node mData;
Mode mMode = Mode::Normal;
};
}
#endif

View file

@ -11,6 +11,7 @@
#include <osg/Multisample>
#include <osg/Texture>
#include <osg/ValueObject>
#include <osg/Capability>
#include <osgParticle/ParticleSystem>
@ -28,6 +29,50 @@
#include "removedalphafunc.hpp"
#include "shadermanager.hpp"
namespace
{
class OpaqueDepthAttribute : public osg::StateAttribute
{
public:
OpaqueDepthAttribute() = default;
OpaqueDepthAttribute(const OpaqueDepthAttribute& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
: osg::StateAttribute(copy, copyop), mTextures(copy.mTextures), mUnit(copy.mUnit) {}
void setTexturesAndUnit(const std::array<osg::ref_ptr<osg::Texture2D>, 2>& textures, int unit)
{
mTextures = textures;
mUnit = unit;
}
META_StateAttribute(Shader, OpaqueDepthAttribute, osg::StateAttribute::TEXTURE)
int compare(const StateAttribute& sa) const override
{
COMPARE_StateAttribute_Types(OpaqueDepthAttribute, sa);
COMPARE_StateAttribute_Parameter(mTextures);
return 0;
}
void apply(osg::State& state) const override
{
auto index = state.getFrameStamp()->getFrameNumber() % 2;
if (!mTextures[index])
return;
state.setActiveTextureUnit(mUnit);
state.applyTextureAttribute(mUnit, mTextures[index]);
}
private:
mutable std::array<osg::ref_ptr<osg::Texture2D>, 2> mTextures;
int mUnit;
};
}
namespace Shader
{
/**
@ -165,6 +210,7 @@ namespace Shader
, mAutoUseSpecularMaps(false)
, mApplyLightingToEnvMaps(false)
, mConvertAlphaTestToAlphaToCoverage(false)
, mSupportsNormalsRT(false)
, mShaderManager(shaderManager)
, mImageManager(imageManager)
, mDefaultShaderPrefix(defaultShaderPrefix)
@ -611,6 +657,14 @@ namespace Shader
defineMap["endLight"] = "0";
}
if (reqs.mAlphaBlend && mSupportsNormalsRT)
{
if (reqs.mSoftParticles)
defineMap["disableNormals"] = "1";
else
writableStateSet->setAttribute(new osg::Disablei(GL_BLEND, 1));
}
if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT && !previousAddedState->hasMode(GL_ALPHA_TEST))
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
// This disables the deprecated fixed-function alpha test
@ -629,7 +683,7 @@ namespace Shader
updateRemovedState(*writableUserData, removedState);
}
if (reqs.mSoftParticles)
if (reqs.mSoftParticles && mOpaqueDepthTex.front())
{
osg::ref_ptr<osg::Depth> depth = new SceneUtil::AutoDepth;
depth->setWriteMask(false);
@ -639,14 +693,18 @@ namespace Shader
writableStateSet->addUniform(new osg::Uniform("particleSize", reqs.mSoftParticleSize));
addedState->addUniform("particleSize");
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", 2));
constexpr int unit = 2;
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", unit));
addedState->addUniform("opaqueDepthTex");
writableStateSet->setTextureAttributeAndModes(2, mOpaqueDepthTex, osg::StateAttribute::ON);
addedState->setTextureAttributeAndModes(2, mOpaqueDepthTex);
osg::ref_ptr<OpaqueDepthAttribute> opaqueDepthAttr = new OpaqueDepthAttribute;
opaqueDepthAttr->setTexturesAndUnit(mOpaqueDepthTex, unit);
writableStateSet->setAttributeAndModes(opaqueDepthAttr, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
addedState->setAttributeAndModes(opaqueDepthAttr);
}
defineMap["softParticles"] = reqs.mSoftParticles ? "1" : "0";
defineMap["softParticles"] = reqs.mSoftParticles && mOpaqueDepthTex.front() ? "1" : "0";
Stereo::Manager::instance().shaderStereoDefines(defineMap);
@ -840,7 +898,7 @@ namespace Shader
{
pushRequirements(drawable);
if (partsys && mOpaqueDepthTex)
if (partsys)
{
mRequirements.back().mSoftParticles = true;
mRequirements.back().mSoftParticleSize = partsys->getDefaultParticleTemplate().getSizeRange().maximum;
@ -915,9 +973,9 @@ namespace Shader
mConvertAlphaTestToAlphaToCoverage = convert;
}
void ShaderVisitor::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture)
void ShaderVisitor::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texturePing, osg::ref_ptr<osg::Texture2D> texturePong)
{
mOpaqueDepthTex = texture;
mOpaqueDepthTex = { texturePing, texturePong };
}
ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets)

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_COMPONENTS_SHADERVISITOR_H
#define OPENMW_COMPONENTS_SHADERVISITOR_H
#include <array>
#include <osg/NodeVisitor>
#include <osg/Program>
#include <osg/Texture2D>
@ -46,7 +48,9 @@ namespace Shader
void setConvertAlphaTestToAlphaToCoverage(bool convert);
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture);
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texturePing, osg::ref_ptr<osg::Texture2D> texturePong);
void setSupportsNormalsRT(bool supports) { mSupportsNormalsRT = supports; }
void apply(osg::Node& node) override;
@ -73,6 +77,8 @@ namespace Shader
bool mConvertAlphaTestToAlphaToCoverage;
bool mSupportsNormalsRT;
ShaderManager& mShaderManager;
Resource::ImageManager& mImageManager;
@ -87,7 +93,7 @@ namespace Shader
bool mShaderRequired;
int mColorMode;
bool mMaterialOverridden;
bool mAlphaTestOverridden;
bool mAlphaBlendOverridden;
@ -116,7 +122,7 @@ namespace Shader
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
osg::ref_ptr<const osg::Program> mProgramTemplate;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
std::array<osg::ref_ptr<osg::Texture2D>, 2> mOpaqueDepthTex;
};
class ReinstateRemovedStateVisitor : public osg::NodeVisitor

162
components/std140/ubo.hpp Normal file
View file

@ -0,0 +1,162 @@
#ifndef COMPONENTS_STD140_UBO_H
#define COMPONENTS_STD140_UBO_H
#include <osg/Vec2f>
#include <osg/Vec4f>
#include <osg/Matrixf>
#include <cstdint>
#include <tuple>
#include <cstring>
#include <string>
#include <string_view>
namespace std140
{
struct Mat4
{
using Value = osg::Matrixf;
Value mValue;
static constexpr size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "mat4";
};
struct Vec4
{
using Value = osg::Vec4f;
Value mValue;
static constexpr size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "vec4";
};
struct Vec3
{
using Value = osg::Vec3f;
Value mValue;
static constexpr std::size_t sAlign = 4 * sizeof(osg::Vec3f::value_type);
static constexpr std::string_view sTypeName = "vec3";
};
struct Vec2
{
using Value = osg::Vec2f;
Value mValue;
static constexpr std::size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "vec2";
};
struct Float
{
using Value = float;
Value mValue;
static constexpr std::size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "float";
};
struct Int
{
using Value = std::int32_t;
Value mValue;
static constexpr std::size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "int";
};
struct UInt
{
using Value = std::uint32_t;
Value mValue;
static constexpr std::size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "uint";
};
struct Bool
{
using Value = std::int32_t;
Value mValue;
static constexpr std::size_t sAlign = sizeof(Value);
static constexpr std::string_view sTypeName = "bool";
};
template <class... CArgs>
class UBO
{
private:
template<typename T, typename... Args>
struct contains : std::bool_constant<(std::is_base_of_v<Args, T> || ...)> { };
static_assert((contains<CArgs, Mat4, Vec4, Vec3, Vec2, Float, Int, UInt, Bool>() && ...));
static constexpr size_t roundUpRemainder(size_t x, size_t multiple)
{
size_t remainder = x % multiple;
if (remainder == 0)
return 0;
return multiple - remainder;
}
template <class T>
static constexpr std::size_t getOffset()
{
bool found = false;
std::size_t size = 0;
((
found = found || std::is_same_v<T, CArgs>,
size += (found ? 0 : sizeof(typename CArgs::Value) + roundUpRemainder(size, CArgs::sAlign))
) , ...);
return size + roundUpRemainder(size, T::sAlign);
}
public:
static constexpr size_t getGPUSize()
{
std::size_t size = 0;
((size += (sizeof(typename CArgs::Value) + roundUpRemainder(size, CArgs::sAlign))), ...);
return size;
}
static std::string getDefinition(const std::string& name)
{
std::string structDefinition = "struct " + name + " {\n";
((structDefinition += (" " + std::string(CArgs::sTypeName) + " " + std::string(CArgs::sName) + ";\n")), ...);
return structDefinition + "};";
}
using BufferType = std::array<char, getGPUSize()>;
using TupleType = std::tuple<CArgs...>;
template <class T>
typename T::Value& get()
{
return std::get<T>(mData).mValue;
}
template <class T>
const typename T::Value& get() const
{
return std::get<T>(mData).mValue;
}
void copyTo(BufferType& buffer) const
{
const auto copy = [&] (const auto& v) {
static_assert(std::is_standard_layout_v<std::decay_t<decltype(v.mValue)>>);
constexpr std::size_t offset = getOffset<std::decay_t<decltype(v)>>();
std::memcpy(buffer.data() + offset, &v.mValue, sizeof(v.mValue));
};
std::apply([&] (const auto& ... v) { (copy(v) , ...); }, mData);
}
const auto& getData() const
{
return mData;
}
private:
std::tuple<CArgs...> mData;
};
}
#endif

View file

@ -3,7 +3,6 @@
#include <osg/FrameBufferObject>
#include <osg/GLExtensions>
#include <osg/Texture2D>
#include <osg/Texture2DMultisample>
#include <osg/Texture2DArray>
#include <osgUtil/RenderStage>
#include <osgUtil/CullVisitor>
@ -322,10 +321,7 @@ namespace Stereo
for (unsigned i = 0; i < 2; i++)
{
if (mSamples > 1)
{
mMsaaColorTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mMsaaColorTexture[i]));
}
mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples)));
mColorTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i]));
}
@ -351,10 +347,7 @@ namespace Stereo
for (unsigned i = 0; i < 2; i++)
{
if (mSamples > 1)
{
mMsaaDepthTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mMsaaDepthTexture[i]));
}
mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples)));
mDepthTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
mLayerFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i]));
}
@ -381,6 +374,11 @@ namespace Stereo
return mMultiviewColorTexture;
}
osg::Texture2DArray* MultiviewFramebuffer::multiviewDepthBuffer()
{
return mMultiviewDepthTexture;
}
osg::Texture2D* MultiviewFramebuffer::layerColorBuffer(int i)
{
return mColorTexture[i];
@ -451,22 +449,6 @@ namespace Stereo
return texture;
}
osg::Texture2DMultisample* MultiviewFramebuffer::createTextureMsaa(GLint sourceFormat, GLint sourceType, GLint internalFormat)
{
osg::Texture2DMultisample* texture = new osg::Texture2DMultisample;
texture->setTextureSize(mWidth, mHeight);
texture->setNumSamples(mSamples);
texture->setSourceFormat(sourceFormat);
texture->setSourceType(sourceType);
texture->setInternalFormat(internalFormat);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
return texture;
}
osg::Texture2DArray* MultiviewFramebuffer::createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat)
{
osg::Texture2DArray* textureArray = new osg::Texture2DArray;

View file

@ -13,7 +13,6 @@ namespace osg
class FrameBufferObject;
class Texture;
class Texture2D;
class Texture2DMultisample;
class Texture2DArray;
}
@ -50,6 +49,7 @@ namespace Stereo
osg::FrameBufferObject* layerFbo(int i);
osg::FrameBufferObject* layerMsaaFbo(int i);
osg::Texture2DArray* multiviewColorBuffer();
osg::Texture2DArray* multiviewDepthBuffer();
osg::Texture2D* layerColorBuffer(int i);
osg::Texture2D* layerDepthBuffer(int i);
@ -62,7 +62,6 @@ namespace Stereo
private:
osg::Texture2D* createTexture(GLint sourceFormat, GLint sourceType, GLint internalFormat);
osg::Texture2DMultisample* createTextureMsaa(GLint sourceFormat, GLint sourceType, GLint internalFormat);
osg::Texture2DArray* createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat);
int mWidth;
@ -76,9 +75,7 @@ namespace Stereo
osg::ref_ptr<osg::Texture2DArray> mMultiviewColorTexture;
osg::ref_ptr<osg::Texture2DArray> mMultiviewDepthTexture;
std::array<osg::ref_ptr<osg::Texture2D>, 2> mColorTexture;
std::array<osg::ref_ptr<osg::Texture2DMultisample>, 2> mMsaaColorTexture;
std::array<osg::ref_ptr<osg::Texture2D>, 2> mDepthTexture;
std::array<osg::ref_ptr<osg::Texture2DMultisample>, 2> mMsaaDepthTexture;
};
}

View file

@ -17,7 +17,6 @@ namespace osg
{
class FrameBufferObject;
class Texture2D;
class Texture2DMultisample;
class Texture2DArray;
}

View file

@ -180,7 +180,7 @@ std::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunk
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale);
return ::Terrain::createPasses(useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale);
}
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry)
@ -268,7 +268,7 @@ osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Ve
layer.mDiffuseMap = compositeMap->mTexture;
layer.mParallax = false;
layer.mSpecular = false;
geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D> >(), 1.f, 1.f));
geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), mSceneManager, std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D> >(), 1.f, 1.f));
}
else
{

View file

@ -6,8 +6,10 @@
#include <osg/Texture2D>
#include <osg/TexMat>
#include <osg/BlendFunc>
#include <osg/Capability>
#include <components/stereo/stereomanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/sceneutil/depth.hpp>
@ -194,9 +196,10 @@ namespace
namespace Terrain
{
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector<TextureLayer> &layers,
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector<TextureLayer> &layers,
const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps, int blendmapScale, float layerTileSize)
{
auto& shaderManager = sceneManager->getShaderManager();
std::vector<osg::ref_ptr<osg::StateSet> > passes;
unsigned int blendmapIndex = 0;
@ -209,6 +212,8 @@ namespace Terrain
if (!blendmaps.empty())
{
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
if (sceneManager->getSupportsNormalsRT())
stateset->setAttribute(new osg::Disablei(GL_BLEND, 1));
stateset->setRenderBinDetails(firstLayer ? 0 : 1, "RenderBin");
if (!firstLayer)
{
@ -251,18 +256,18 @@ namespace Terrain
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0";
Stereo::Manager::instance().shaderStereoDefines(defineMap);
osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
osg::ref_ptr<osg::Shader> vertexShader = shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
if (!vertexShader || !fragmentShader)
{
// Try again without shader. Error already logged by above
return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);
return createPasses(false, sceneManager, layers, blendmaps, blendmapScale, layerTileSize);
}
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));
stateset->setAttributeAndModes(shaderManager.getProgram(vertexShader, fragmentShader));
stateset->addUniform(UniformCollection::value().mColorMode);
}
else

View file

@ -10,9 +10,9 @@ namespace osg
class Texture2D;
}
namespace Shader
namespace Resource
{
class ShaderManager;
class SceneManager;
}
namespace Terrain
@ -26,7 +26,7 @@ namespace Terrain
bool mSpecular;
};
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Shader::ShaderManager* shaderManager,
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Resource::SceneManager* sceneManager,
const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);

View file

@ -14,6 +14,8 @@ namespace VFS
virtual ~File() {}
virtual Files::IStreamPtr open() = 0;
virtual std::string getPath() = 0;
};
class Archive

View file

@ -15,6 +15,8 @@ namespace VFS
Files::IStreamPtr open() override;
std::string getPath() override { return mInfo->name(); }
const Bsa::BSAFile::FileStruct* mInfo;
Bsa::BSAFile* mFile;
};
@ -26,6 +28,8 @@ namespace VFS
Files::IStreamPtr open() override;
std::string getPath() override { return mInfo->name(); }
const Bsa::BSAFile::FileStruct* mInfo;
Bsa::CompressedBSAFile* mCompressedFile;
};

View file

@ -13,6 +13,8 @@ namespace VFS
Files::IStreamPtr open() override;
std::string getPath() override { return mPath; }
private:
std::string mPath;

View file

@ -105,6 +105,17 @@ namespace VFS
return {};
}
std::string Manager::getAbsoluteFileName(const std::string& name) const
{
std::string normalized = name;
normalize_path(normalized, mStrict);
std::map<std::string, File*>::const_iterator found = mIndex.find(normalized);
if (found == mIndex.end())
throw std::runtime_error("Resource '" + normalized + "' not found");
return found->second->getPath();
}
namespace
{
bool startsWith(std::string_view text, std::string_view start)

View file

@ -90,6 +90,11 @@ namespace VFS
/// @note May be called from any thread once the index has been built.
RecursiveDirectoryRange getRecursiveDirectoryIterator(const std::string& path) const;
/// Retrieve the absolute path to the file
/// @note Throws an exception if the file can not be found.
/// @note May be called from any thread once the index has been built.
std::string getAbsoluteFileName(const std::string& name) const;
private:
bool mStrict;