diff --git a/assets/opensb/interface/opensb/shaders/select.png b/assets/opensb/interface/opensb/shaders/select.png new file mode 100644 index 0000000..dc649b4 Binary files /dev/null and b/assets/opensb/interface/opensb/shaders/select.png differ diff --git a/assets/opensb/interface/opensb/shaders/selectend.png b/assets/opensb/interface/opensb/shaders/selectend.png new file mode 100644 index 0000000..99908d6 Binary files /dev/null and b/assets/opensb/interface/opensb/shaders/selectend.png differ diff --git a/assets/opensb/interface/opensb/shaders/shaders.config b/assets/opensb/interface/opensb/shaders/shaders.config index 5154831..864574e 100644 --- a/assets/opensb/interface/opensb/shaders/shaders.config +++ b/assets/opensb/interface/opensb/shaders/shaders.config @@ -3,7 +3,10 @@ "scriptDelta" : 0, "scriptWidgetCallbacks" : [ "selectGroup", - "toggleGroupEnabled" + "toggleGroupEnabled", + "sliderOptionModified", + "textboxOptionModified", + "selectOptionPressed" ], "gui" : { diff --git a/assets/opensb/interface/opensb/shaders/shaders.lua b/assets/opensb/interface/opensb/shaders/shaders.lua index 0030b5c..0c2d19c 100644 --- a/assets/opensb/interface/opensb/shaders/shaders.lua +++ b/assets/opensb/interface/opensb/shaders/shaders.lua @@ -17,6 +17,14 @@ local groups = {} local widgetsToGroups = {} local allSettings = {} +local shaderConfig + +function displayed() + shaderConfig = root.getConfiguration("postProcessGroups") +end +function dismissed() + shaderConfig = nil +end local function addGroupToList(data) local name = widget.addListItem(GROUP_LIST_WIDGET) @@ -67,7 +75,7 @@ local function addOptionGroup(data, i, added) added[#added + 1] = name local label = { type = "label", - value = data.name, + value = data.label, wrapWidth = 120, fontSize = 8, hAnchor = "mid", @@ -77,6 +85,16 @@ local function addOptionGroup(data, i, added) widget.addChild(fmt("%s.%s", BINDS_WIDGET, name), label, "text") 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 y = (i - 1) * -14 local bg = { @@ -84,12 +102,12 @@ local function addOption(data, i, added) file = PATH .. "optionname.png", position = {-12, y} } - local name = "label_" .. i + local name = "label_" .. data.name widget.addChild(OPTIONS_WIDGET, bg, name) added[#added + 1] = name local label = { type = "label", - value = data.name, + value = data.label, wrapWidth = 120, fontSize = 8, hAnchor = "mid", @@ -97,7 +115,115 @@ local function addOption(data, i, added) position = {62, 6} } 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 + 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 local function addEnabled(groupname, i, added) @@ -139,6 +265,7 @@ end function toggleGroupEnabled(checkbox, cdata) renderer.setPostProcessGroupEnabled(activeGroup, widget.getChecked(fmt("%s.%s",OPTIONS_WIDGET,checkbox)), true) + shaderConfig = root.getConfiguration("postProcessGroups") end function selectGroup() @@ -163,7 +290,21 @@ function selectGroup() local added = {} local index = 0 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 {} if not categories.unsorted then @@ -198,7 +339,8 @@ function selectGroup() addOption(category.sortedOptions[iB], index, added) end end - end]] + end + ]] widget.setData(OPTIONS_WIDGET, added) end diff --git a/assets/opensb/scripts/opensb/universeclient/loadconfig.lua b/assets/opensb/scripts/opensb/universeclient/loadconfig.lua new file mode 100644 index 0000000..2bb3ef8 --- /dev/null +++ b/assets/opensb/scripts/opensb/universeclient/loadconfig.lua @@ -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 diff --git a/assets/opensb/scripts/opensb/universeclient/universeclient.lua b/assets/opensb/scripts/opensb/universeclient/universeclient.lua index cad342b..a534c25 100644 --- a/assets/opensb/scripts/opensb/universeclient/universeclient.lua +++ b/assets/opensb/scripts/opensb/universeclient/universeclient.lua @@ -1,2 +1,2 @@ require "/scripts/opensb/util/modules.lua" -modules("/scripts/opensb/universeclient/", {"voicemanager"}) \ No newline at end of file +modules("/scripts/opensb/universeclient/", {"voicemanager","loadconfig"}) diff --git a/source/application/StarRenderer.hpp b/source/application/StarRenderer.hpp index 2a156f7..2b106ff 100644 --- a/source/application/StarRenderer.hpp +++ b/source/application/StarRenderer.hpp @@ -120,7 +120,7 @@ public: virtual void set(List& primitives) = 0; }; -typedef Variant RenderEffectParameter; +typedef Variant RenderEffectParameter; class Renderer { public: @@ -141,6 +141,9 @@ public: // The effect config will specify named parameters and textures which can be // set here. 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 getEffectScriptableParameter(String const& effectName, String const& parameterName) = 0; + virtual Maybe getEffectScriptableParameterType(String const& effectName, String const& parameterName) = 0; virtual void setEffectTexture(String const& textureName, ImageView const& image) = 0; virtual bool switchEffectConfig(String const& name) = 0; diff --git a/source/application/StarRenderer_opengl.cpp b/source/application/StarRenderer_opengl.cpp index 9d0d717..ceb945d 100644 --- a/source/application/StarRenderer_opengl.cpp +++ b/source/application/StarRenderer_opengl.cpp @@ -308,21 +308,39 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf throw RendererException::format("Unrecognized effect parameter type '{}'", type); } - effect.parameters[p.first] = effectParameter; - - if (Json def = p.second.get("default", {})) { - if (type == "bool") { - setEffectParameter(p.first, def.toBool()); - } else if (type == "int") { - setEffectParameter(p.first, (int)def.toInt()); - } else if (type == "float") { - setEffectParameter(p.first, def.toFloat()); - } else if (type == "vec2") { - setEffectParameter(p.first, jsonToVec2F(def)); - } else if (type == "vec3") { - setEffectParameter(p.first, jsonToVec3F(def)); - } else if (type == "vec4") { - setEffectParameter(p.first, jsonToVec4F(def)); + 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; + if (Json def = p.second.get("default", {})) { + if (type == "bool") { + setEffectParameter(p.first, def.toBool()); + } else if (type == "int") { + setEffectParameter(p.first, (int)def.toInt()); + } else if (type == "float") { + setEffectParameter(p.first, def.toFloat()); + } else if (type == "vec2") { + setEffectParameter(p.first, jsonToVec2F(def)); + } else if (type == "vec3") { + setEffectParameter(p.first, jsonToVec3F(def)); + } else if (type == "vec4") { + setEffectParameter(p.first, jsonToVec4F(def)); + } } } } @@ -384,6 +402,50 @@ void OpenGlRenderer::setEffectParameter(String const& parameterName, RenderEffec 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 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 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) { auto ptr = m_currentEffect->textures.ptr(textureName); if (!ptr) @@ -1063,6 +1125,26 @@ void OpenGlRenderer::setupGlUniforms(Effect& effect, Vec2U screenSize) { } glUniform2f(m_screenSizeUniform, screenSize[0], screenSize[1]); + + for (auto& param : effect.scriptables) { + auto ptr = ¶m.second; + auto mvalue = ptr->parameterValue; + if (mvalue) { + RenderEffectParameter value = mvalue.value(); + if (auto v = value.ptr()) + glUniform1i(ptr->parameterUniform, *v); + else if (auto v = value.ptr()) + glUniform1i(ptr->parameterUniform, *v); + else if (auto v = value.ptr()) + glUniform1f(ptr->parameterUniform, *v); + else if (auto v = value.ptr()) + glUniform2f(ptr->parameterUniform, (*v)[0], (*v)[1]); + else if (auto v = value.ptr()) + glUniform3f(ptr->parameterUniform, (*v)[0], (*v)[1], (*v)[2]); + else if (auto v = value.ptr()) + glUniform4f(ptr->parameterUniform, (*v)[0], (*v)[1], (*v)[2], (*v)[3]); + } + } } RefPtr OpenGlRenderer::getGlFrameBuffer(String const& id) { diff --git a/source/application/StarRenderer_opengl.hpp b/source/application/StarRenderer_opengl.hpp index 36b8354..79e5400 100644 --- a/source/application/StarRenderer_opengl.hpp +++ b/source/application/StarRenderer_opengl.hpp @@ -25,6 +25,9 @@ public: void loadEffectConfig(String const& name, Json const& effectConfig, StringMap const& shaders) override; void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override; + void setEffectScriptableParameter(String const& effectName, String const& parameterName, RenderEffectParameter const& parameter) override; + Maybe getEffectScriptableParameter(String const& effectName, String const& parameterName) override; + Maybe getEffectScriptableParameterType(String const& effectName, String const& parameterName) override; void setEffectTexture(String const& textureName, ImageView const& image) override; void setScissorRect(Maybe const& scissorRect) override; @@ -191,6 +194,7 @@ private: GLuint program = 0; Json config; StringMap parameters; + StringMap scriptables; // scriptable parameters which can be changed when the effect is not loaded StringMap textures; StringMap attributes; diff --git a/source/client/StarRenderingLuaBindings.cpp b/source/client/StarRenderingLuaBindings.cpp index 4c9e7a2..39dc1a1 100644 --- a/source/client/StarRenderingLuaBindings.cpp +++ b/source/client/StarRenderingLuaBindings.cpp @@ -1,6 +1,8 @@ #include "StarRenderingLuaBindings.hpp" +#include "StarJsonExtra.hpp" #include "StarLuaConverters.hpp" #include "StarClientApplication.hpp" +#include "StarRenderer.hpp" namespace Star { @@ -11,8 +13,30 @@ LuaCallbacks LuaBindings::makeRenderingCallbacks(ClientApplication* app) { callbacks.registerCallbackWithSignature>("setPostProcessGroupEnabled", bind(mem_fn(&ClientApplication::setPostProcessGroupEnabled), app, _1, _2, _3)); callbacks.registerCallbackWithSignature("postProcessGroupEnabled", bind(mem_fn(&ClientApplication::postProcessGroupEnabled), app, _1)); + // not entirely necessary (root.assetJson can achieve the same purpose) but may as well callbacks.registerCallbackWithSignature("postProcessGroups", bind(mem_fn(&ClientApplication::postProcessGroups), app)); + + // typedef Variant 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()) { + renderer->setEffectScriptableParameter(effectName, effectParameter, (int)value.get()); + } 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; } diff --git a/source/core/StarLuaConverters.hpp b/source/core/StarLuaConverters.hpp index d9dfc67..c79cbdc 100644 --- a/source/core/StarLuaConverters.hpp +++ b/source/core/StarLuaConverters.hpp @@ -168,11 +168,11 @@ struct LuaConverter> { template struct LuaConverter> { static LuaValue from(LuaEngine& engine, Variant 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&& 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> to(LuaEngine& engine, LuaValue const& v) { @@ -217,7 +217,7 @@ struct LuaConverter> { static LuaValue from(LuaEngine& engine, MVariant const& variant) { LuaValue value; variant.call([&value, &engine](auto const& a) { - value = luaFrom(engine, a); + value = engine.luaFrom(a); }); return value; } @@ -225,7 +225,7 @@ struct LuaConverter> { static LuaValue from(LuaEngine& engine, MVariant&& variant) { LuaValue value; variant.call([&value, &engine](auto& a) { - value = luaFrom(engine, std::move(a)); + value = engine.luaFrom(std::move(a)); }); return value; } diff --git a/source/windowing/StarScrollArea.cpp b/source/windowing/StarScrollArea.cpp index f4cc140..775462a 100644 --- a/source/windowing/StarScrollArea.cpp +++ b/source/windowing/StarScrollArea.cpp @@ -384,7 +384,8 @@ bool ScrollArea::sendEvent(InputEvent const& event) { return true; } -void ScrollArea::update(float) { +void ScrollArea::update(float dt) { + Widget::update(dt); if (!m_visible) return;