Merge pull request #158 from Bottinator22/main

Scriptable shader uniforms
This commit is contained in:
Kae 2024-12-29 15:29:20 +11:00 committed by GitHub
commit b953042e56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 309 additions and 28 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -3,7 +3,10 @@
"scriptDelta" : 0, "scriptDelta" : 0,
"scriptWidgetCallbacks" : [ "scriptWidgetCallbacks" : [
"selectGroup", "selectGroup",
"toggleGroupEnabled" "toggleGroupEnabled",
"sliderOptionModified",
"textboxOptionModified",
"selectOptionPressed"
], ],
"gui" : { "gui" : {

View File

@ -17,6 +17,14 @@ local groups = {}
local widgetsToGroups = {} local widgetsToGroups = {}
local allSettings = {} local allSettings = {}
local shaderConfig
function displayed()
shaderConfig = root.getConfiguration("postProcessGroups")
end
function dismissed()
shaderConfig = nil
end
local function addGroupToList(data) local function addGroupToList(data)
local name = widget.addListItem(GROUP_LIST_WIDGET) local name = widget.addListItem(GROUP_LIST_WIDGET)
@ -67,7 +75,7 @@ local function addOptionGroup(data, i, added)
added[#added + 1] = name added[#added + 1] = name
local label = { local label = {
type = "label", type = "label",
value = data.name, value = data.label,
wrapWidth = 120, wrapWidth = 120,
fontSize = 8, fontSize = 8,
hAnchor = "mid", hAnchor = "mid",
@ -77,6 +85,16 @@ local function addOptionGroup(data, i, added)
widget.addChild(fmt("%s.%s", BINDS_WIDGET, name), label, "text") widget.addChild(fmt("%s.%s", BINDS_WIDGET, name), label, "text")
end end
local function digitRegex(n)
local i = 0
while n ~= math.floor(n) do
n = n * 10
i = i + 1
end
return fmt("%%.%df",i) -- create format string %.nf, where n = %d (i with no decimal points)
end
local optionPrefix = "option_"
local optionOffset = #optionPrefix+1
local function addOption(data, i, added) local function addOption(data, i, added)
local y = (i - 1) * -14 local y = (i - 1) * -14
local bg = { local bg = {
@ -84,12 +102,12 @@ local function addOption(data, i, added)
file = PATH .. "optionname.png", file = PATH .. "optionname.png",
position = {-12, y} position = {-12, y}
} }
local name = "label_" .. i local name = "label_" .. data.name
widget.addChild(OPTIONS_WIDGET, bg, name) widget.addChild(OPTIONS_WIDGET, bg, name)
added[#added + 1] = name added[#added + 1] = name
local label = { local label = {
type = "label", type = "label",
value = data.name, value = data.label,
wrapWidth = 120, wrapWidth = 120,
fontSize = 8, fontSize = 8,
hAnchor = "mid", hAnchor = "mid",
@ -97,7 +115,115 @@ local function addOption(data, i, added)
position = {62, 6} position = {62, 6}
} }
widget.addChild(fmt("%s.%s", OPTIONS_WIDGET, name), label, "text") widget.addChild(fmt("%s.%s", OPTIONS_WIDGET, name), label, "text")
local value = data.default or 0
if not shaderConfig[activeGroup] then
shaderConfig[activeGroup] = {parameters={}}
end
if not shaderConfig[activeGroup].parameters then
shaderConfig[activeGroup].parameters = {}
end
if shaderConfig[activeGroup].parameters[data.name] ~= nil then
value = shaderConfig[activeGroup].parameters[data.name]
end
-- todo: finish this -- todo: finish this
if data.type == "slider" then
local range = data.range or {0,1}
local delta = data.delta or 0.01
-- for some reason ranges require ints so
local r = {range[1]/delta, range[2]/delta, 1}
local slider = {
type = "slider",
callback = "sliderOptionModified",
position = {110, y + 2},
gridImage = "/interface/optionsmenu/smallselection.png",
range = r
}
name = optionPrefix..data.name
added[#added + 1] = name
widget.addChild(OPTIONS_WIDGET, slider, name)
widget.setSliderValue(fmt("%s.%s",OPTIONS_WIDGET,name), value/delta)
local valLabel = {
type = "label",
value = fmt(digitRegex(delta),value),
position = {186, y + 2}
}
added[#added + 1] = name.."_value"
widget.addChild(OPTIONS_WIDGET, valLabel, name.."_value")
elseif data.type == "select" then
local n = #data.values
local width = math.floor(118/n)
local by = y
local buttons = {}
for i=0,n-1 do
local img = fmt("%sselect.png?crop=0;0;%.0f;13",PATH,width)
local button = {
type = "button",
callback="selectOptionPressed",
caption = data.values[i+1],
base = img,
hover = img.."?brightness=-25",
baseImageChecked = img.."?multiply=00ff00",
hoverImageChecked = img.."?multiply=00ff00?brightness=-25",
position = {110+width*i, by},
pressedOffset = {0, 0},
checkable = true,
checked = (value == i)
}
name = "select_"..data.name.."_"..i
added[#added + 1] = name
widget.addChild(OPTIONS_WIDGET, button, name)
table.insert(buttons,{widget=fmt("%s.%s",OPTIONS_WIDGET,name),index=i})
end
for k,v in next, buttons do
widget.setData(v.widget,{option=data,index=v.index,buttons=buttons})
end
--[[local bge = {
type = "image",
file = PATH.."selectend.png",
position = {110+width*3, by}
}
name = "bgend_"..data.name
added[#added + 1] = name
widget.addChild(OPTIONS_WIDGET, bge, name)]]
end
end
function sliderOptionModified(option, odata)
local oname = string.sub(option, optionOffset)
local parameter = groups[activeGroup].parameters[oname]
local value = widget.getSliderValue(fmt("%s.%s",OPTIONS_WIDGET,option))*parameter.delta
widget.setText(fmt("%s.%s",OPTIONS_WIDGET,option.."_value"), fmt(digitRegex(parameter.delta),value))
for k,v in next, parameter.effects do
renderer.setEffectParameter(v, oname, value)
end
shaderConfig[activeGroup].parameters[oname] = value
root.setConfiguration("postProcessGroups",shaderConfig)
end
function selectOptionPressed(button,bdata)
sb.logInfo(sb.print(bdata))
for k,v in next, bdata.buttons do
if v.index ~= bdata.index then
widget.setChecked(v.widget, false)
end
end
value = bdata.index
local oname = bdata.option.name
local parameter = groups[activeGroup].parameters[oname]
for k,v in next, parameter.effects do
renderer.setEffectParameter(v, oname, value)
end
shaderConfig[activeGroup].parameters[oname] = value
root.setConfiguration("postProcessGroups",shaderConfig)
end end
local function addEnabled(groupname, i, added) local function addEnabled(groupname, i, added)
@ -139,6 +265,7 @@ end
function toggleGroupEnabled(checkbox, cdata) function toggleGroupEnabled(checkbox, cdata)
renderer.setPostProcessGroupEnabled(activeGroup, widget.getChecked(fmt("%s.%s",OPTIONS_WIDGET,checkbox)), true) renderer.setPostProcessGroupEnabled(activeGroup, widget.getChecked(fmt("%s.%s",OPTIONS_WIDGET,checkbox)), true)
shaderConfig = root.getConfiguration("postProcessGroups")
end end
function selectGroup() function selectGroup()
@ -163,7 +290,21 @@ function selectGroup()
local added = {} local added = {}
local index = 0 local index = 0
addEnabled(group.groupId,index,added) addEnabled(group.groupId,index,added)
if group.categories then
elseif group.parameters then
local sortedParams = {}
for k,v in next, group.parameters do
v.name = k
sortedParams[#sortedParams+1] = v
end
table.sort(sortedParams, alphabeticalNameSortLesser)
for k,v in next, sortedParams do
index = index + 1
addOption(v, index, added)
end
end
--[[ --[[
local categories = group.categories or {} local categories = group.categories or {}
if not categories.unsorted then if not categories.unsorted then
@ -198,7 +339,8 @@ function selectGroup()
addOption(category.sortedOptions[iB], index, added) addOption(category.sortedOptions[iB], index, added)
end end
end end
end]] end
]]
widget.setData(OPTIONS_WIDGET, added) widget.setData(OPTIONS_WIDGET, added)
end end

View File

@ -0,0 +1,22 @@
-- Meant to manage loading various miscellaneous things from configuration, such as shader parameters.
local module = {}
modules.config_loader = module
function module.init()
local shaderConfig = root.getConfiguration("postProcessGroups") or {}
local postProcessGroups = renderer.postProcessGroups()
local changes = false
for k,v in next, shaderConfig do
local group = postProcessGroups[k]
if v.parameters then
for k2,v2 in next, group.parameters do
if v.parameters[k2] ~= nil then
for _,e in next, v2.effects do
renderer.setEffectParameter(e,k2,v.parameters[k2])
end
end
end
end
end
end

View File

@ -1,2 +1,2 @@
require "/scripts/opensb/util/modules.lua" require "/scripts/opensb/util/modules.lua"
modules("/scripts/opensb/universeclient/", {"voicemanager"}) modules("/scripts/opensb/universeclient/", {"voicemanager","loadconfig"})

View File

@ -120,7 +120,7 @@ public:
virtual void set(List<RenderPrimitive>& primitives) = 0; virtual void set(List<RenderPrimitive>& primitives) = 0;
}; };
typedef Variant<bool, int, float, Vec2F, Vec3F, Vec4F> RenderEffectParameter; typedef Variant<float, int, Vec2F, Vec3F, Vec4F, bool> RenderEffectParameter;
class Renderer { class Renderer {
public: public:
@ -141,6 +141,9 @@ public:
// The effect config will specify named parameters and textures which can be // The effect config will specify named parameters and textures which can be
// set here. // set here.
virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0; virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0;
virtual void setEffectScriptableParameter(String const& effectName, String const& parameterName, RenderEffectParameter const& parameter) = 0;
virtual Maybe<RenderEffectParameter> getEffectScriptableParameter(String const& effectName, String const& parameterName) = 0;
virtual Maybe<VariantTypeIndex> getEffectScriptableParameterType(String const& effectName, String const& parameterName) = 0;
virtual void setEffectTexture(String const& textureName, ImageView const& image) = 0; virtual void setEffectTexture(String const& textureName, ImageView const& image) = 0;
virtual bool switchEffectConfig(String const& name) = 0; virtual bool switchEffectConfig(String const& name) = 0;

View File

@ -308,8 +308,25 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf
throw RendererException::format("Unrecognized effect parameter type '{}'", type); throw RendererException::format("Unrecognized effect parameter type '{}'", type);
} }
if (p.second.getBool("scriptable",false)) {
if (Json def = p.second.get("default", {})) {
if (type == "bool") {
effectParameter.parameterValue = (RenderEffectParameter)def.toBool();
} else if (type == "int") {
effectParameter.parameterValue = (RenderEffectParameter)(int)def.toInt();
} else if (type == "float") {
effectParameter.parameterValue = (RenderEffectParameter)def.toFloat();
} else if (type == "vec2") {
effectParameter.parameterValue = (RenderEffectParameter)jsonToVec2F(def);
} else if (type == "vec3") {
effectParameter.parameterValue = (RenderEffectParameter)jsonToVec3F(def);
} else if (type == "vec4") {
effectParameter.parameterValue = (RenderEffectParameter)jsonToVec4F(def);
}
}
effect.scriptables[p.first] = effectParameter;
} else {
effect.parameters[p.first] = effectParameter; effect.parameters[p.first] = effectParameter;
if (Json def = p.second.get("default", {})) { if (Json def = p.second.get("default", {})) {
if (type == "bool") { if (type == "bool") {
setEffectParameter(p.first, def.toBool()); setEffectParameter(p.first, def.toBool());
@ -327,6 +344,7 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf
} }
} }
} }
}
// Assign each texture parameter a texture unit starting with MultiTextureCount, the first // Assign each texture parameter a texture unit starting with MultiTextureCount, the first
// few texture units are used by the primary textures being drawn. Currently, // few texture units are used by the primary textures being drawn. Currently,
@ -384,6 +402,50 @@ void OpenGlRenderer::setEffectParameter(String const& parameterName, RenderEffec
ptr->parameterValue = value; ptr->parameterValue = value;
} }
void OpenGlRenderer::setEffectScriptableParameter(String const& effectName, String const& parameterName, RenderEffectParameter const& value) {
auto find = m_effects.find(effectName);
if (find == m_effects.end())
return;
Effect& effect = find->second;
auto ptr = effect.scriptables.ptr(parameterName);
if (!ptr || (ptr->parameterValue && *ptr->parameterValue == value))
return;
if (ptr->parameterType != value.typeIndex())
throw RendererException::format("OpenGlRenderer::setEffectScriptableParameter '{}' parameter type mismatch", parameterName);
ptr->parameterValue = value;
}
Maybe<RenderEffectParameter> OpenGlRenderer::getEffectScriptableParameter(String const& effectName, String const& parameterName) {
auto find = m_effects.find(effectName);
if (find == m_effects.end())
return {};
Effect& effect = find->second;
auto ptr = effect.scriptables.ptr(parameterName);
if (!ptr)
return {};
return ptr->parameterValue;
}
Maybe<VariantTypeIndex> OpenGlRenderer::getEffectScriptableParameterType(String const& effectName, String const& parameterName) {
auto find = m_effects.find(effectName);
if (find == m_effects.end())
return {};
Effect& effect = find->second;
auto ptr = effect.scriptables.ptr(parameterName);
if (!ptr)
return {};
return ptr->parameterType;
}
void OpenGlRenderer::setEffectTexture(String const& textureName, ImageView const& image) { void OpenGlRenderer::setEffectTexture(String const& textureName, ImageView const& image) {
auto ptr = m_currentEffect->textures.ptr(textureName); auto ptr = m_currentEffect->textures.ptr(textureName);
if (!ptr) if (!ptr)
@ -1063,6 +1125,26 @@ void OpenGlRenderer::setupGlUniforms(Effect& effect, Vec2U screenSize) {
} }
glUniform2f(m_screenSizeUniform, screenSize[0], screenSize[1]); glUniform2f(m_screenSizeUniform, screenSize[0], screenSize[1]);
for (auto& param : effect.scriptables) {
auto ptr = &param.second;
auto mvalue = ptr->parameterValue;
if (mvalue) {
RenderEffectParameter value = mvalue.value();
if (auto v = value.ptr<bool>())
glUniform1i(ptr->parameterUniform, *v);
else if (auto v = value.ptr<int>())
glUniform1i(ptr->parameterUniform, *v);
else if (auto v = value.ptr<float>())
glUniform1f(ptr->parameterUniform, *v);
else if (auto v = value.ptr<Vec2F>())
glUniform2f(ptr->parameterUniform, (*v)[0], (*v)[1]);
else if (auto v = value.ptr<Vec3F>())
glUniform3f(ptr->parameterUniform, (*v)[0], (*v)[1], (*v)[2]);
else if (auto v = value.ptr<Vec4F>())
glUniform4f(ptr->parameterUniform, (*v)[0], (*v)[1], (*v)[2], (*v)[3]);
}
}
} }
RefPtr<OpenGlRenderer::GlFrameBuffer> OpenGlRenderer::getGlFrameBuffer(String const& id) { RefPtr<OpenGlRenderer::GlFrameBuffer> OpenGlRenderer::getGlFrameBuffer(String const& id) {

View File

@ -25,6 +25,9 @@ public:
void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) override; void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) override;
void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override; void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override;
void setEffectScriptableParameter(String const& effectName, String const& parameterName, RenderEffectParameter const& parameter) override;
Maybe<RenderEffectParameter> getEffectScriptableParameter(String const& effectName, String const& parameterName) override;
Maybe<VariantTypeIndex> getEffectScriptableParameterType(String const& effectName, String const& parameterName) override;
void setEffectTexture(String const& textureName, ImageView const& image) override; void setEffectTexture(String const& textureName, ImageView const& image) override;
void setScissorRect(Maybe<RectI> const& scissorRect) override; void setScissorRect(Maybe<RectI> const& scissorRect) override;
@ -191,6 +194,7 @@ private:
GLuint program = 0; GLuint program = 0;
Json config; Json config;
StringMap<EffectParameter> parameters; StringMap<EffectParameter> parameters;
StringMap<EffectParameter> scriptables; // scriptable parameters which can be changed when the effect is not loaded
StringMap<EffectTexture> textures; StringMap<EffectTexture> textures;
StringMap<GLuint> attributes; StringMap<GLuint> attributes;

View File

@ -1,6 +1,8 @@
#include "StarRenderingLuaBindings.hpp" #include "StarRenderingLuaBindings.hpp"
#include "StarJsonExtra.hpp"
#include "StarLuaConverters.hpp" #include "StarLuaConverters.hpp"
#include "StarClientApplication.hpp" #include "StarClientApplication.hpp"
#include "StarRenderer.hpp"
namespace Star { namespace Star {
@ -11,9 +13,31 @@ LuaCallbacks LuaBindings::makeRenderingCallbacks(ClientApplication* app) {
callbacks.registerCallbackWithSignature<void, String, bool, Maybe<bool>>("setPostProcessGroupEnabled", bind(mem_fn(&ClientApplication::setPostProcessGroupEnabled), app, _1, _2, _3)); callbacks.registerCallbackWithSignature<void, String, bool, Maybe<bool>>("setPostProcessGroupEnabled", bind(mem_fn(&ClientApplication::setPostProcessGroupEnabled), app, _1, _2, _3));
callbacks.registerCallbackWithSignature<bool, String>("postProcessGroupEnabled", bind(mem_fn(&ClientApplication::postProcessGroupEnabled), app, _1)); callbacks.registerCallbackWithSignature<bool, String>("postProcessGroupEnabled", bind(mem_fn(&ClientApplication::postProcessGroupEnabled), app, _1));
// not entirely necessary (root.assetJson can achieve the same purpose) but may as well // not entirely necessary (root.assetJson can achieve the same purpose) but may as well
callbacks.registerCallbackWithSignature<Json>("postProcessGroups", bind(mem_fn(&ClientApplication::postProcessGroups), app)); callbacks.registerCallbackWithSignature<Json>("postProcessGroups", bind(mem_fn(&ClientApplication::postProcessGroups), app));
// typedef Variant<float, int, Vec2F, Vec3F, Vec4F, bool> RenderEffectParameter;
// feel free to change this if there's a better way to do this
// specifically checks if the effect parameter is an int since Lua prefers converting the values to floats
callbacks.registerCallback("setEffectParameter", [app](String const& effectName, String const& effectParameter, RenderEffectParameter const& value) {
auto renderer = app->renderer();
auto mtype = renderer->getEffectScriptableParameterType(effectName, effectParameter);
if (mtype) {
auto type = mtype.value();
if (type == 1 && value.is<float>()) {
renderer->setEffectScriptableParameter(effectName, effectParameter, (int)value.get<float>());
} else {
renderer->setEffectScriptableParameter(effectName, effectParameter, value);
}
}
});
callbacks.registerCallback("getEffectParameter", [app](String const& effectName, String const& effectParameter) {
auto renderer = app->renderer();
return renderer->getEffectScriptableParameter(effectName, effectParameter);
});
return callbacks; return callbacks;
} }

View File

@ -168,11 +168,11 @@ struct LuaConverter<Line<T, N>> {
template <typename FirstType, typename... RestTypes> template <typename FirstType, typename... RestTypes>
struct LuaConverter<Variant<FirstType, RestTypes...>> { struct LuaConverter<Variant<FirstType, RestTypes...>> {
static LuaValue from(LuaEngine& engine, Variant<FirstType, RestTypes...> const& variant) { static LuaValue from(LuaEngine& engine, Variant<FirstType, RestTypes...> const& variant) {
return variant.call([&engine](auto const& a) { return luaFrom(engine, a); }); return variant.call([&engine](auto const& a) { return engine.luaFrom(a); });
} }
static LuaValue from(LuaEngine& engine, Variant<FirstType, RestTypes...>&& variant) { static LuaValue from(LuaEngine& engine, Variant<FirstType, RestTypes...>&& variant) {
return variant.call([&engine](auto& a) { return luaFrom(engine, std::move(a)); }); return variant.call([&engine](auto& a) { return engine.luaFrom(std::move(a)); });
} }
static Maybe<Variant<FirstType, RestTypes...>> to(LuaEngine& engine, LuaValue const& v) { static Maybe<Variant<FirstType, RestTypes...>> to(LuaEngine& engine, LuaValue const& v) {
@ -217,7 +217,7 @@ struct LuaConverter<MVariant<Types...>> {
static LuaValue from(LuaEngine& engine, MVariant<Types...> const& variant) { static LuaValue from(LuaEngine& engine, MVariant<Types...> const& variant) {
LuaValue value; LuaValue value;
variant.call([&value, &engine](auto const& a) { variant.call([&value, &engine](auto const& a) {
value = luaFrom(engine, a); value = engine.luaFrom(a);
}); });
return value; return value;
} }
@ -225,7 +225,7 @@ struct LuaConverter<MVariant<Types...>> {
static LuaValue from(LuaEngine& engine, MVariant<Types...>&& variant) { static LuaValue from(LuaEngine& engine, MVariant<Types...>&& variant) {
LuaValue value; LuaValue value;
variant.call([&value, &engine](auto& a) { variant.call([&value, &engine](auto& a) {
value = luaFrom(engine, std::move(a)); value = engine.luaFrom(std::move(a));
}); });
return value; return value;
} }

View File

@ -384,7 +384,8 @@ bool ScrollArea::sendEvent(InputEvent const& event) {
return true; return true;
} }
void ScrollArea::update(float) { void ScrollArea::update(float dt) {
Widget::update(dt);
if (!m_visible) if (!m_visible)
return; return;