Merge pull request #158 from Bottinator22/main
Scriptable shader uniforms
This commit is contained in:
commit
b953042e56
BIN
assets/opensb/interface/opensb/shaders/select.png
Normal file
BIN
assets/opensb/interface/opensb/shaders/select.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/opensb/interface/opensb/shaders/selectend.png
Normal file
BIN
assets/opensb/interface/opensb/shaders/selectend.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
@ -3,7 +3,10 @@
|
||||
"scriptDelta" : 0,
|
||||
"scriptWidgetCallbacks" : [
|
||||
"selectGroup",
|
||||
"toggleGroupEnabled"
|
||||
"toggleGroupEnabled",
|
||||
"sliderOptionModified",
|
||||
"textboxOptionModified",
|
||||
"selectOptionPressed"
|
||||
],
|
||||
|
||||
"gui" : {
|
||||
|
@ -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
|
||||
|
22
assets/opensb/scripts/opensb/universeclient/loadconfig.lua
Normal file
22
assets/opensb/scripts/opensb/universeclient/loadconfig.lua
Normal 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
|
@ -1,2 +1,2 @@
|
||||
require "/scripts/opensb/util/modules.lua"
|
||||
modules("/scripts/opensb/universeclient/", {"voicemanager"})
|
||||
modules("/scripts/opensb/universeclient/", {"voicemanager","loadconfig"})
|
||||
|
@ -120,7 +120,7 @@ public:
|
||||
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 {
|
||||
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<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 bool switchEffectConfig(String const& name) = 0;
|
||||
|
||||
|
@ -308,8 +308,25 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf
|
||||
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;
|
||||
|
||||
if (Json def = p.second.get("default", {})) {
|
||||
if (type == "bool") {
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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<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) {
|
||||
|
@ -25,6 +25,9 @@ public:
|
||||
void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> 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<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 setScissorRect(Maybe<RectI> const& scissorRect) override;
|
||||
@ -191,6 +194,7 @@ private:
|
||||
GLuint program = 0;
|
||||
Json config;
|
||||
StringMap<EffectParameter> parameters;
|
||||
StringMap<EffectParameter> scriptables; // scriptable parameters which can be changed when the effect is not loaded
|
||||
StringMap<EffectTexture> textures;
|
||||
|
||||
StringMap<GLuint> attributes;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "StarRenderingLuaBindings.hpp"
|
||||
#include "StarJsonExtra.hpp"
|
||||
#include "StarLuaConverters.hpp"
|
||||
#include "StarClientApplication.hpp"
|
||||
#include "StarRenderer.hpp"
|
||||
|
||||
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<bool, String>("postProcessGroupEnabled", bind(mem_fn(&ClientApplication::postProcessGroupEnabled), app, _1));
|
||||
|
||||
|
||||
// not entirely necessary (root.assetJson can achieve the same purpose) but may as well
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -168,11 +168,11 @@ struct LuaConverter<Line<T, N>> {
|
||||
template <typename FirstType, typename... RestTypes>
|
||||
struct LuaConverter<Variant<FirstType, RestTypes...>> {
|
||||
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) {
|
||||
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) {
|
||||
@ -217,7 +217,7 @@ struct LuaConverter<MVariant<Types...>> {
|
||||
static LuaValue from(LuaEngine& engine, MVariant<Types...> 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<MVariant<Types...>> {
|
||||
static LuaValue from(LuaEngine& engine, MVariant<Types...>&& variant) {
|
||||
LuaValue value;
|
||||
variant.call([&value, &engine](auto& a) {
|
||||
value = luaFrom(engine, std::move(a));
|
||||
value = engine.luaFrom(std::move(a));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user