mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-30 12:32:36 +00:00
moddable post-processing pipeline
This commit is contained in:
parent
e7fb8b6fd8
commit
04843fed6d
@ -128,6 +128,7 @@
|
|||||||
Feature #2858: Add a tab to the launcher for handling datafolders
|
Feature #2858: Add a tab to the launcher for handling datafolders
|
||||||
Feature #3245: Grid and angle snapping for the OpenMW-CS
|
Feature #3245: Grid and angle snapping for the OpenMW-CS
|
||||||
Feature #3616: Allow Zoom levels on the World Map
|
Feature #3616: Allow Zoom levels on the World Map
|
||||||
|
Feature #4067: Post Processing
|
||||||
Feature #4297: Implement APPLIED_ONCE flag for magic effects
|
Feature #4297: Implement APPLIED_ONCE flag for magic effects
|
||||||
Feature #4414: Handle duration of EXTRA SPELL magic effect
|
Feature #4414: Handle duration of EXTRA SPELL magic effect
|
||||||
Feature #4595: Unique object identifier
|
Feature #4595: Unique object identifier
|
||||||
|
@ -145,6 +145,12 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||||||
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
|
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
|
||||||
|
|
||||||
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||||
|
|
||||||
|
connect(postprocessEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPostProcessToggled(bool)));
|
||||||
|
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||||
|
loadSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
||||||
|
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||||
|
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("hdr exposure time", "Post Processing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
@ -302,6 +308,11 @@ void Launcher::AdvancedPage::saveSettings()
|
|||||||
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
|
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
|
||||||
|
|
||||||
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||||
|
|
||||||
|
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||||
|
saveSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
||||||
|
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||||
|
Settings::Manager::setDouble("hdr exposure time", "Post Processing", postprocessHDRTimeComboBox->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
@ -464,3 +475,11 @@ void Launcher::AdvancedPage::slotViewOverShoulderToggled(bool checked)
|
|||||||
{
|
{
|
||||||
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
|
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
|
||||||
|
{
|
||||||
|
postprocessLiveReloadCheckBox->setEnabled(checked);
|
||||||
|
postprocessTransparentPostpassCheckBox->setEnabled(checked);
|
||||||
|
postprocessHDRTimeComboBox->setEnabled(checked);
|
||||||
|
postprocessHDRTimeLabel->setEnabled(checked);
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ namespace Launcher
|
|||||||
void on_runScriptAfterStartupBrowseButton_clicked();
|
void on_runScriptAfterStartupBrowseButton_clicked();
|
||||||
void slotAnimSourcesToggled(bool checked);
|
void slotAnimSourcesToggled(bool checked);
|
||||||
void slotViewOverShoulderToggled(bool checked);
|
void slotViewOverShoulderToggled(bool checked);
|
||||||
|
void slotPostProcessToggled(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config::GameSettings &mGameSettings;
|
Config::GameSettings &mGameSettings;
|
||||||
|
@ -84,6 +84,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
|
|||||||
defines["radialFog"] = "0";
|
defines["radialFog"] = "0";
|
||||||
defines["lightingModel"] = "0";
|
defines["lightingModel"] = "0";
|
||||||
defines["reverseZ"] = "0";
|
defines["reverseZ"] = "0";
|
||||||
|
defines["refraction_enabled"] = "0";
|
||||||
for (const auto& define : shadowDefines)
|
for (const auto& define : shadowDefines)
|
||||||
defines[define.first] = define.second;
|
defines[define.first] = define.second;
|
||||||
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
|
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
|
||||||
|
@ -22,7 +22,8 @@ add_openmw_dir (mwrender
|
|||||||
actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation vismask
|
actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation vismask
|
||||||
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager
|
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager
|
||||||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
||||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor
|
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover
|
||||||
|
postprocessor pingpongcull hdr pingpongcanvas transparentpass
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwinput
|
add_openmw_dir (mwinput
|
||||||
@ -43,6 +44,7 @@ add_openmw_dir (mwgui
|
|||||||
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
||||||
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
||||||
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
||||||
|
postprocessorhud
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwdialogue
|
add_openmw_dir (mwdialogue
|
||||||
@ -59,7 +61,7 @@ add_openmw_dir (mwscript
|
|||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp object worldview userdataserializer eventqueue
|
luamanagerimp object worldview userdataserializer eventqueue
|
||||||
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
|
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
|
||||||
camerabindings uibindings inputbindings nearbybindings stats
|
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats
|
||||||
types/types types/door types/actor types/container types/weapon types/npc types/creature
|
types/types types/door types/actor types/container types/weapon types/npc types/creature
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
#include <components/sceneutil/color.hpp>
|
#include <components/sceneutil/color.hpp>
|
||||||
#include <components/sceneutil/util.hpp>
|
#include <components/sceneutil/util.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/shadermanager.hpp>
|
||||||
|
|
||||||
#include "mwinput/inputmanagerimp.hpp"
|
#include "mwinput/inputmanagerimp.hpp"
|
||||||
|
|
||||||
#include "mwgui/windowmanagerimp.hpp"
|
#include "mwgui/windowmanagerimp.hpp"
|
||||||
@ -987,6 +989,8 @@ void OMW::Engine::go()
|
|||||||
Settings::Manager settings;
|
Settings::Manager settings;
|
||||||
std::string settingspath = settings.load(mCfgMgr);
|
std::string settingspath = settings.load(mCfgMgr);
|
||||||
|
|
||||||
|
Settings::ShaderManager::get().load((mCfgMgr.getUserConfigPath() / "shaders.yaml").string());
|
||||||
|
|
||||||
MWClass::registerClasses();
|
MWClass::registerClasses();
|
||||||
|
|
||||||
// Create encoder
|
// Create encoder
|
||||||
@ -1110,6 +1114,7 @@ void OMW::Engine::go()
|
|||||||
|
|
||||||
// Save user settings
|
// Save user settings
|
||||||
settings.saveUser(settingspath);
|
settings.saveUser(settingspath);
|
||||||
|
Settings::ShaderManager::get().save();
|
||||||
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath().string());
|
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath().string());
|
||||||
|
|
||||||
Log(Debug::Info) << "Quitting peacefully.";
|
Log(Debug::Info) << "Quitting peacefully.";
|
||||||
|
@ -70,6 +70,7 @@ namespace MWGui
|
|||||||
class WindowModal;
|
class WindowModal;
|
||||||
class JailScreen;
|
class JailScreen;
|
||||||
class MessageBox;
|
class MessageBox;
|
||||||
|
class PostProcessorHud;
|
||||||
|
|
||||||
enum ShowInDialogueMode {
|
enum ShowInDialogueMode {
|
||||||
ShowInDialogueMode_IfPossible,
|
ShowInDialogueMode_IfPossible,
|
||||||
@ -147,6 +148,7 @@ namespace MWBase
|
|||||||
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
|
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
|
||||||
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
||||||
virtual const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() = 0;
|
virtual const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() = 0;
|
||||||
|
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
|
virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
|
||||||
@ -326,6 +328,7 @@ namespace MWBase
|
|||||||
|
|
||||||
virtual void toggleConsole() = 0;
|
virtual void toggleConsole() = 0;
|
||||||
virtual void toggleDebugWindow() = 0;
|
virtual void toggleDebugWindow() = 0;
|
||||||
|
virtual void togglePostProcessorHud() = 0;
|
||||||
|
|
||||||
/// Cycle to next or previous spell
|
/// Cycle to next or previous spell
|
||||||
virtual void cycleSpell(bool next) = 0;
|
virtual void cycleSpell(bool next) = 0;
|
||||||
|
@ -67,6 +67,7 @@ namespace MWRender
|
|||||||
class Animation;
|
class Animation;
|
||||||
class Camera;
|
class Camera;
|
||||||
class RenderingManager;
|
class RenderingManager;
|
||||||
|
class PostProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
@ -238,6 +239,10 @@ namespace MWBase
|
|||||||
|
|
||||||
virtual int getCurrentWeather() const = 0;
|
virtual int getCurrentWeather() const = 0;
|
||||||
|
|
||||||
|
virtual int getNextWeather() const = 0;
|
||||||
|
|
||||||
|
virtual float getWeatherTransition() const = 0;
|
||||||
|
|
||||||
virtual unsigned int getNightDayMode() const = 0;
|
virtual unsigned int getNightDayMode() const = 0;
|
||||||
|
|
||||||
virtual int getMasserPhase() const = 0;
|
virtual int getMasserPhase() const = 0;
|
||||||
@ -664,6 +669,8 @@ namespace MWBase
|
|||||||
virtual Misc::Rng::Generator& getPrng() = 0;
|
virtual Misc::Rng::Generator& getPrng() = 0;
|
||||||
|
|
||||||
virtual MWRender::RenderingManager* getRenderingManager() = 0;
|
virtual MWRender::RenderingManager* getRenderingManager() = 0;
|
||||||
|
|
||||||
|
virtual MWRender::PostProcessor* getPostProcessor() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
454
apps/openmw/mwgui/postprocessorhud.cpp
Normal file
454
apps/openmw/mwgui/postprocessorhud.cpp
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
#include "postprocessorhud.hpp"
|
||||||
|
|
||||||
|
#include <MyGUI_Window.h>
|
||||||
|
#include <MyGUI_Button.h>
|
||||||
|
#include <MyGUI_TabItem.h>
|
||||||
|
#include <MyGUI_TabControl.h>
|
||||||
|
#include <MyGUI_RenderManager.h>
|
||||||
|
#include <MyGUI_FactoryManager.h>
|
||||||
|
|
||||||
|
#include <components/fx/widgets.hpp>
|
||||||
|
#include <components/fx/technique.hpp>
|
||||||
|
|
||||||
|
#include <components/widgets/box.hpp>
|
||||||
|
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void saveChain()
|
||||||
|
{
|
||||||
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
|
std::ostringstream chain;
|
||||||
|
|
||||||
|
for (size_t i = 1; i < processor->getTechniques().size(); ++i)
|
||||||
|
{
|
||||||
|
auto technique = processor->getTechniques()[i];
|
||||||
|
|
||||||
|
if (!technique)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chain << technique->getName();
|
||||||
|
|
||||||
|
if (i < processor-> getTechniques().size() - 1)
|
||||||
|
chain << ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::Manager::setString("chain", "Post Processing", chain.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWGui
|
||||||
|
{
|
||||||
|
void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch)
|
||||||
|
{
|
||||||
|
if (MyGUI::InputManager::getInstance().isShiftPressed() && (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MyGUI::ListBox::onKeyButtonPressed(key, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessorHud::PostProcessorHud()
|
||||||
|
: WindowBase("openmw_postprocessor_hud.layout")
|
||||||
|
{
|
||||||
|
getWidget(mTabConfiguration, "TabConfiguration");
|
||||||
|
getWidget(mActiveList, "ActiveList");
|
||||||
|
getWidget(mInactiveList, "InactiveList");
|
||||||
|
getWidget(mModeToggle, "ModeToggle");
|
||||||
|
getWidget(mConfigLayout, "ConfigLayout");
|
||||||
|
getWidget(mFilter, "Filter");
|
||||||
|
getWidget(mButtonActivate, "ButtonActivate");
|
||||||
|
getWidget(mButtonDeactivate, "ButtonDeactivate");
|
||||||
|
getWidget(mButtonUp, "ButtonUp");
|
||||||
|
getWidget(mButtonDown, "ButtonDown");
|
||||||
|
|
||||||
|
mButtonActivate->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyActivatePressed);
|
||||||
|
mButtonDeactivate->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyDeactivatePressed);
|
||||||
|
mButtonUp->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderUpPressed);
|
||||||
|
mButtonDown->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderDownPressed);
|
||||||
|
|
||||||
|
mActiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
|
||||||
|
mInactiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
|
||||||
|
|
||||||
|
mActiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
|
||||||
|
mInactiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
|
||||||
|
|
||||||
|
mModeToggle->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyModeToggle);
|
||||||
|
|
||||||
|
mFilter->eventEditTextChange += MyGUI::newDelegate(this, &PostProcessorHud::notifyFilterChanged);
|
||||||
|
|
||||||
|
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &PostProcessorHud::notifyWindowResize);
|
||||||
|
|
||||||
|
mShaderInfo = mConfigLayout->createWidget<Gui::AutoSizedEditBox>("HeaderText", {}, MyGUI::Align::Default);
|
||||||
|
mShaderInfo->setUserString("VStretch", "true");
|
||||||
|
mShaderInfo->setUserString("HStretch", "true");
|
||||||
|
mShaderInfo->setTextAlign(MyGUI::Align::Left | MyGUI::Align::Top);
|
||||||
|
mShaderInfo->setEditReadOnly(true);
|
||||||
|
mShaderInfo->setEditWordWrap(true);
|
||||||
|
mShaderInfo->setEditMultiLine(true);
|
||||||
|
|
||||||
|
mConfigLayout->setVisibleVScroll(true);
|
||||||
|
|
||||||
|
mConfigArea = mConfigLayout->createWidget<MyGUI::Widget>("", {}, MyGUI::Align::Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyFilterChanged(MyGUI::EditBox* sender)
|
||||||
|
{
|
||||||
|
updateTechniques();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyWindowResize(MyGUI::Window* sender)
|
||||||
|
{
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyResetButtonClicked(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
for (size_t i = 1; i < mConfigArea->getChildCount(); ++i)
|
||||||
|
{
|
||||||
|
if (auto* child = dynamic_cast<fx::Widgets::UniformBase*>(mConfigArea->getChildAt(i)))
|
||||||
|
child->toDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyListChangePosition(MyGUI::ListBox* sender, size_t index)
|
||||||
|
{
|
||||||
|
if (sender == mActiveList)
|
||||||
|
mInactiveList->clearIndexSelected();
|
||||||
|
else if (sender == mInactiveList)
|
||||||
|
mActiveList->clearIndexSelected();
|
||||||
|
|
||||||
|
if (index >= sender->getItemCount())
|
||||||
|
return;
|
||||||
|
|
||||||
|
updateConfigView(sender->getItemNameAt(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::toggleTechnique(bool enabled)
|
||||||
|
{
|
||||||
|
auto* list = enabled ? mInactiveList : mActiveList;
|
||||||
|
|
||||||
|
size_t selected = list->getIndexSelected();
|
||||||
|
|
||||||
|
if (selected != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
mOverrideHint = list->getItemNameAt(selected);
|
||||||
|
if (enabled)
|
||||||
|
processor->enableTechnique(*list->getItemDataAt<std::shared_ptr<fx::Technique>>(selected));
|
||||||
|
else
|
||||||
|
processor->disableTechnique(*list->getItemDataAt<std::shared_ptr<fx::Technique>>(selected));
|
||||||
|
saveChain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyActivatePressed(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
toggleTechnique(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyDeactivatePressed(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
toggleTechnique(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::moveShader(Direction direction)
|
||||||
|
{
|
||||||
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
|
size_t selected = mActiveList->getIndexSelected();
|
||||||
|
|
||||||
|
if (selected == MyGUI::ITEM_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = direction == Direction::Up ? static_cast<int>(selected) - 1 : selected + 1;
|
||||||
|
index = std::clamp<int>(index, 0, mActiveList->getItemCount() - 1);
|
||||||
|
|
||||||
|
if (static_cast<size_t>(index) != selected)
|
||||||
|
{
|
||||||
|
if (processor->enableTechnique(*mActiveList->getItemDataAt<std::shared_ptr<fx::Technique>>(selected), index))
|
||||||
|
saveChain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyShaderUpPressed(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
moveShader(Direction::Up);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyShaderDownPressed(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
moveShader(Direction::Down);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char ch)
|
||||||
|
{
|
||||||
|
MyGUI::ListBox* list = static_cast<MyGUI::ListBox*>(sender);
|
||||||
|
|
||||||
|
if (list->getIndexSelected() == MyGUI::ITEM_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (key == MyGUI::KeyCode::ArrowLeft && list == mActiveList)
|
||||||
|
{
|
||||||
|
if (MyGUI::InputManager::getInstance().isShiftPressed())
|
||||||
|
{
|
||||||
|
toggleTechnique(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mInactiveList);
|
||||||
|
mActiveList->clearIndexSelected();
|
||||||
|
select(mInactiveList, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == MyGUI::KeyCode::ArrowRight && list == mInactiveList)
|
||||||
|
{
|
||||||
|
if (MyGUI::InputManager::getInstance().isShiftPressed())
|
||||||
|
{
|
||||||
|
toggleTechnique(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mActiveList);
|
||||||
|
mInactiveList->clearIndexSelected();
|
||||||
|
select(mActiveList, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (list == mActiveList && MyGUI::InputManager::getInstance().isShiftPressed() && (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
|
||||||
|
{
|
||||||
|
moveShader(key == MyGUI::KeyCode::ArrowUp ? Direction::Up : Direction::Down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::notifyModeToggle(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
Settings::ShaderManager::Mode prev = Settings::ShaderManager::get().getMode();
|
||||||
|
toggleMode(prev == Settings::ShaderManager::Mode::Debug ? Settings::ShaderManager::Mode::Normal : Settings::ShaderManager::Mode::Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::onOpen()
|
||||||
|
{
|
||||||
|
toggleMode(Settings::ShaderManager::Mode::Debug);
|
||||||
|
updateTechniques();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::onClose()
|
||||||
|
{
|
||||||
|
toggleMode(Settings::ShaderManager::Mode::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::layout()
|
||||||
|
{
|
||||||
|
constexpr int padding = 12;
|
||||||
|
constexpr int padding2 = padding * 2;
|
||||||
|
mShaderInfo->setCoord(padding, padding, mConfigLayout->getSize().width - padding2 - padding, mShaderInfo->getTextSize().height);
|
||||||
|
|
||||||
|
int totalHeight = mShaderInfo->getTop() + mShaderInfo->getTextSize().height + padding;
|
||||||
|
|
||||||
|
mConfigArea->setCoord({padding, totalHeight, mShaderInfo->getSize().width, mConfigLayout->getHeight()});
|
||||||
|
|
||||||
|
int childHeights = 0;
|
||||||
|
MyGUI::EnumeratorWidgetPtr enumerator = mConfigArea->getEnumerator();
|
||||||
|
while (enumerator.next())
|
||||||
|
{
|
||||||
|
enumerator.current()->setCoord(padding, childHeights + padding, mShaderInfo->getSize().width - padding2, enumerator.current()->getHeight());
|
||||||
|
childHeights += enumerator.current()->getHeight() + padding;
|
||||||
|
}
|
||||||
|
totalHeight += childHeights;
|
||||||
|
|
||||||
|
mConfigArea->setSize(mConfigArea->getWidth(), childHeights);
|
||||||
|
|
||||||
|
mConfigLayout->setCanvasSize(mConfigLayout->getWidth() - padding2, totalHeight);
|
||||||
|
mConfigLayout->setSize(mConfigLayout->getWidth(), mConfigLayout->getParentSize().height - padding2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::select(ListWrapper* list, size_t index)
|
||||||
|
{
|
||||||
|
list->setIndexSelected(index);
|
||||||
|
notifyListChangePosition(list, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::toggleMode(Settings::ShaderManager::Mode mode)
|
||||||
|
{
|
||||||
|
Settings::ShaderManager::get().setMode(mode);
|
||||||
|
|
||||||
|
mModeToggle->setCaptionWithReplacing(mode == Settings::ShaderManager::Mode::Debug ? "#{sOn}" :"#{sOff}");
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->getPostProcessor()->toggleMode();
|
||||||
|
|
||||||
|
if (!isVisible())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
|
updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected()));
|
||||||
|
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
|
updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::updateConfigView(const std::string& name)
|
||||||
|
{
|
||||||
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
|
auto technique = processor->loadTechnique(name);
|
||||||
|
|
||||||
|
if (!technique)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (mConfigArea->getChildCount() > 0)
|
||||||
|
MyGUI::Gui::getInstance().destroyWidget(mConfigArea->getChildAt(0));
|
||||||
|
|
||||||
|
mShaderInfo->setCaption("");
|
||||||
|
|
||||||
|
if (!technique)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
|
||||||
|
const std::string NA = "NA";
|
||||||
|
const std::string endl = "\n";
|
||||||
|
|
||||||
|
std::string author = technique->getAuthor().empty() ? NA : std::string(technique->getAuthor());
|
||||||
|
std::string version = technique->getVersion().empty() ? NA : std::string(technique->getVersion());
|
||||||
|
std::string description = technique->getDescription().empty() ? NA : std::string(technique->getDescription());
|
||||||
|
|
||||||
|
auto serializeBool = [](bool value) {
|
||||||
|
return value ? "#{sYes}" : "#{sNo}";
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto flags = technique->getFlags();
|
||||||
|
|
||||||
|
const auto flag_interior = serializeBool (!(flags & fx::Technique::Flag_Disable_Interiors));
|
||||||
|
const auto flag_exterior = serializeBool (!(flags & fx::Technique::Flag_Disable_Exteriors));
|
||||||
|
const auto flag_underwater = serializeBool(!(flags & fx::Technique::Flag_Disable_Underwater));
|
||||||
|
const auto flag_abovewater = serializeBool(!(flags & fx::Technique::Flag_Disable_Abovewater));
|
||||||
|
|
||||||
|
switch (technique->getStatus())
|
||||||
|
{
|
||||||
|
case fx::Technique::Status::Success:
|
||||||
|
case fx::Technique::Status::Uncompiled:
|
||||||
|
ss << "#{fontcolourhtml=header}Author: #{fontcolourhtml=normal} " << author << endl << endl
|
||||||
|
<< "#{fontcolourhtml=header}Version: #{fontcolourhtml=normal} " << version << endl << endl
|
||||||
|
<< "#{fontcolourhtml=header}Description: #{fontcolourhtml=normal} " << description << endl << endl
|
||||||
|
<< "#{fontcolourhtml=header}Interiors: #{fontcolourhtml=normal} " << flag_interior
|
||||||
|
<< "#{fontcolourhtml=header} Exteriors: #{fontcolourhtml=normal} " << flag_exterior
|
||||||
|
<< "#{fontcolourhtml=header} Underwater: #{fontcolourhtml=normal} " << flag_underwater
|
||||||
|
<< "#{fontcolourhtml=header} Abovewater: #{fontcolourhtml=normal} " << flag_abovewater;
|
||||||
|
break;
|
||||||
|
case fx::Technique::Status::File_Not_exists:
|
||||||
|
ss << "#{fontcolourhtml=negative}Shader Error: #{fontcolourhtml=header} <" << std::string(technique->getFileName()) << ">#{fontcolourhtml=normal} not found." << endl << endl
|
||||||
|
<< "Ensure the shader file is in a 'Shaders/' sub directory in a data files directory";
|
||||||
|
break;
|
||||||
|
case fx::Technique::Status::Parse_Error:
|
||||||
|
ss << "#{fontcolourhtml=negative}Shader Compile Error: #{fontcolourhtml=normal} <" << std::string(technique->getName()) << "> failed to compile." << endl << endl
|
||||||
|
<< technique->getLastError();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mShaderInfo->setCaptionWithReplacing(ss.str());
|
||||||
|
|
||||||
|
if (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug)
|
||||||
|
{
|
||||||
|
if (technique->getUniformMap().size() > 0)
|
||||||
|
{
|
||||||
|
MyGUI::Button* resetButton = mConfigArea->createWidget<MyGUI::Button>("MW_Button", {0,0,0,24}, MyGUI::Align::Default);
|
||||||
|
resetButton->setCaption("Reset all to default");
|
||||||
|
resetButton->setTextAlign(MyGUI::Align::Center);
|
||||||
|
resetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyResetButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& uniform : technique->getUniformMap())
|
||||||
|
{
|
||||||
|
if (!uniform->mStatic || uniform->mSamplerType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!uniform->mHeader.empty())
|
||||||
|
mConfigArea->createWidget<Gui::AutoSizedTextBox>("MW_UniformGroup", {0,0,0,34}, MyGUI::Align::Default)->setCaption(uniform->mHeader);
|
||||||
|
|
||||||
|
fx::Widgets::UniformBase* uwidget = mConfigArea->createWidget<fx::Widgets::UniformBase>("MW_UniformEdit", {0,0,0,22}, MyGUI::Align::Default);
|
||||||
|
uwidget->init(uniform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::updateTechniques()
|
||||||
|
{
|
||||||
|
if (!isVisible())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string hint;
|
||||||
|
ListWrapper* hintWidget = nullptr;
|
||||||
|
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
hint = mInactiveList->getItemNameAt(mInactiveList->getIndexSelected());
|
||||||
|
hintWidget = mInactiveList;
|
||||||
|
}
|
||||||
|
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
hint = mActiveList->getItemNameAt(mActiveList->getIndexSelected());
|
||||||
|
hintWidget = mActiveList;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInactiveList->removeAllItems();
|
||||||
|
mActiveList->removeAllItems();
|
||||||
|
|
||||||
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
|
for (const auto& [name, _] : processor->getTechniqueMap())
|
||||||
|
{
|
||||||
|
auto technique = processor->loadTechnique(name);
|
||||||
|
|
||||||
|
if (!technique)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!technique->getHidden() && !processor->isTechniqueEnabled(technique) && name.find(mFilter->getCaption()) != std::string::npos)
|
||||||
|
mInactiveList->addItem(name, technique);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto technique : processor->getTechniques())
|
||||||
|
{
|
||||||
|
if (!technique->getHidden())
|
||||||
|
mActiveList->addItem(technique->getName(), technique);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tryFocus = [this](ListWrapper* widget, const std::string& hint)
|
||||||
|
{
|
||||||
|
size_t index = widget->findItemIndexWith(hint);
|
||||||
|
|
||||||
|
if (index != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(widget);
|
||||||
|
select(widget, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!mOverrideHint.empty())
|
||||||
|
{
|
||||||
|
tryFocus(mActiveList, mOverrideHint);
|
||||||
|
tryFocus(mInactiveList, mOverrideHint);
|
||||||
|
|
||||||
|
mOverrideHint.clear();
|
||||||
|
}
|
||||||
|
else if (hintWidget && !hint.empty())
|
||||||
|
tryFocus(hintWidget, hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessorHud::registerMyGUIComponents()
|
||||||
|
{
|
||||||
|
MyGUI::FactoryManager& factory = MyGUI::FactoryManager::getInstance();
|
||||||
|
factory.registerFactory<fx::Widgets::UniformBase>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditNumberFloat4>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditNumberFloat3>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditNumberFloat2>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditNumberFloat>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditNumberInt>("Widget");
|
||||||
|
factory.registerFactory<fx::Widgets::EditBool>("Widget");
|
||||||
|
factory.registerFactory<ListWrapper>("Widget");
|
||||||
|
}
|
||||||
|
}
|
107
apps/openmw/mwgui/postprocessorhud.hpp
Normal file
107
apps/openmw/mwgui/postprocessorhud.hpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#ifndef MYGUI_POSTPROCESSOR_HUD_H
|
||||||
|
#define MYGUI_POSTPROCESSOR_HUD_H
|
||||||
|
|
||||||
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
|
#include <MyGUI_Gui.h>
|
||||||
|
#include <MyGUI_ListBox.h>
|
||||||
|
|
||||||
|
#include <components/settings/shadermanager.hpp>
|
||||||
|
|
||||||
|
namespace MyGUI
|
||||||
|
{
|
||||||
|
class ScrollView;
|
||||||
|
class EditBox;
|
||||||
|
class TabItem;
|
||||||
|
}
|
||||||
|
namespace Gui
|
||||||
|
{
|
||||||
|
class AutoSizedButton;
|
||||||
|
class AutoSizedEditBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWGui
|
||||||
|
{
|
||||||
|
class PostProcessorHud : public WindowBase
|
||||||
|
{
|
||||||
|
class ListWrapper final : public MyGUI::ListBox
|
||||||
|
{
|
||||||
|
MYGUI_RTTI_DERIVED(ListWrapper)
|
||||||
|
protected:
|
||||||
|
void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostProcessorHud();
|
||||||
|
|
||||||
|
void onOpen() override;
|
||||||
|
|
||||||
|
void onClose() override;
|
||||||
|
|
||||||
|
void updateTechniques();
|
||||||
|
|
||||||
|
void toggleMode(Settings::ShaderManager::Mode mode);
|
||||||
|
|
||||||
|
static void registerMyGUIComponents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void notifyWindowResize(MyGUI::Window* sender);
|
||||||
|
|
||||||
|
void notifyFilterChanged(MyGUI::EditBox* sender);
|
||||||
|
|
||||||
|
void updateConfigView(const std::string& name);
|
||||||
|
|
||||||
|
void notifyModeToggle(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
void notifyResetButtonClicked(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
void notifyListChangePosition(MyGUI::ListBox* sender, size_t index);
|
||||||
|
|
||||||
|
void notifyKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char ch);
|
||||||
|
|
||||||
|
void notifyActivatePressed(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
void notifyDeactivatePressed(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
void notifyShaderUpPressed(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
void notifyShaderDownPressed(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
Up,
|
||||||
|
Down
|
||||||
|
};
|
||||||
|
|
||||||
|
void moveShader(Direction direction);
|
||||||
|
|
||||||
|
void toggleTechnique(bool enabled);
|
||||||
|
|
||||||
|
void select(ListWrapper* list, size_t index);
|
||||||
|
|
||||||
|
void layout();
|
||||||
|
|
||||||
|
MyGUI::TabItem* mTabConfiguration;
|
||||||
|
|
||||||
|
ListWrapper* mActiveList;
|
||||||
|
ListWrapper* mInactiveList;
|
||||||
|
|
||||||
|
Gui::AutoSizedButton* mButtonActivate;
|
||||||
|
Gui::AutoSizedButton* mButtonDeactivate;
|
||||||
|
Gui::AutoSizedButton* mButtonDown;
|
||||||
|
Gui::AutoSizedButton* mButtonUp;
|
||||||
|
|
||||||
|
MyGUI::ScrollView* mConfigLayout;
|
||||||
|
|
||||||
|
MyGUI::Widget* mConfigArea;
|
||||||
|
|
||||||
|
MyGUI::EditBox* mFilter;
|
||||||
|
Gui::AutoSizedButton* mModeToggle;
|
||||||
|
Gui::AutoSizedEditBox* mShaderInfo;
|
||||||
|
|
||||||
|
std::string mOverrideHint;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -71,6 +71,7 @@
|
|||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
#include "../mwrender/localmap.hpp"
|
#include "../mwrender/localmap.hpp"
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
#include "journalwindow.hpp"
|
#include "journalwindow.hpp"
|
||||||
@ -113,6 +114,7 @@
|
|||||||
#include "itemwidget.hpp"
|
#include "itemwidget.hpp"
|
||||||
#include "screenfader.hpp"
|
#include "screenfader.hpp"
|
||||||
#include "debugwindow.hpp"
|
#include "debugwindow.hpp"
|
||||||
|
#include "postprocessorhud.hpp"
|
||||||
#include "spellview.hpp"
|
#include "spellview.hpp"
|
||||||
#include "draganddrop.hpp"
|
#include "draganddrop.hpp"
|
||||||
#include "container.hpp"
|
#include "container.hpp"
|
||||||
@ -164,6 +166,7 @@ namespace MWGui
|
|||||||
, mHitFader(nullptr)
|
, mHitFader(nullptr)
|
||||||
, mScreenFader(nullptr)
|
, mScreenFader(nullptr)
|
||||||
, mDebugWindow(nullptr)
|
, mDebugWindow(nullptr)
|
||||||
|
, mPostProcessorHud(nullptr)
|
||||||
, mJailScreen(nullptr)
|
, mJailScreen(nullptr)
|
||||||
, mContainerWindow(nullptr)
|
, mContainerWindow(nullptr)
|
||||||
, mTranslationDataStorage (translationDataStorage)
|
, mTranslationDataStorage (translationDataStorage)
|
||||||
@ -217,7 +220,8 @@ namespace MWGui
|
|||||||
MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>("Widget");
|
MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>("Widget");
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::AdditiveLayer>("Layer");
|
MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::AdditiveLayer>("Layer");
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer");
|
MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer");
|
||||||
BookPage::registerMyGUIComponents ();
|
BookPage::registerMyGUIComponents();
|
||||||
|
PostProcessorHud::registerMyGUIComponents();
|
||||||
ItemView::registerComponents();
|
ItemView::registerComponents();
|
||||||
ItemChargeView::registerComponents();
|
ItemChargeView::registerComponents();
|
||||||
ItemWidget::registerComponents();
|
ItemWidget::registerComponents();
|
||||||
@ -469,6 +473,10 @@ namespace MWGui
|
|||||||
mDebugWindow = new DebugWindow();
|
mDebugWindow = new DebugWindow();
|
||||||
mWindows.push_back(mDebugWindow);
|
mWindows.push_back(mDebugWindow);
|
||||||
|
|
||||||
|
mPostProcessorHud = new PostProcessorHud();
|
||||||
|
mWindows.push_back(mPostProcessorHud);
|
||||||
|
trackWindow(mPostProcessorHud, "postprocessor");
|
||||||
|
|
||||||
mInputBlocker = MyGUI::Gui::getInstance().createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Stretch,"InputBlocker");
|
mInputBlocker = MyGUI::Gui::getInstance().createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Stretch,"InputBlocker");
|
||||||
|
|
||||||
mHud->setVisible(true);
|
mHud->setVisible(true);
|
||||||
@ -897,6 +905,8 @@ namespace MWGui
|
|||||||
|
|
||||||
mDebugWindow->onFrame(frameDuration);
|
mDebugWindow->onFrame(frameDuration);
|
||||||
|
|
||||||
|
mPostProcessorHud->onFrame(frameDuration);
|
||||||
|
|
||||||
if (mCharGen)
|
if (mCharGen)
|
||||||
mCharGen->onFrame(frameDuration);
|
mCharGen->onFrame(frameDuration);
|
||||||
|
|
||||||
@ -1400,6 +1410,7 @@ namespace MWGui
|
|||||||
MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; }
|
MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; }
|
||||||
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
|
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
|
||||||
MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
|
MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
|
||||||
|
MWGui::PostProcessorHud* WindowManager::getPostProcessorHud() { return mPostProcessorHud; }
|
||||||
|
|
||||||
void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
|
void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
|
||||||
{
|
{
|
||||||
@ -1488,6 +1499,7 @@ namespace MWGui
|
|||||||
return
|
return
|
||||||
!mGuiModes.empty() ||
|
!mGuiModes.empty() ||
|
||||||
isConsoleMode() ||
|
isConsoleMode() ||
|
||||||
|
(mPostProcessorHud && mPostProcessorHud->isVisible()) ||
|
||||||
(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());
|
(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2054,6 +2066,24 @@ namespace MWGui
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::togglePostProcessorHud()
|
||||||
|
{
|
||||||
|
if (!MWBase::Environment::get().getWorld()->getPostProcessor()->isEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool visible = mPostProcessorHud->isVisible();
|
||||||
|
|
||||||
|
if (!visible && !mGuiModes.empty())
|
||||||
|
mKeyboardNavigation->saveFocus(mGuiModes.back());
|
||||||
|
|
||||||
|
mPostProcessorHud->setVisible(!visible);
|
||||||
|
|
||||||
|
if (visible && !mGuiModes.empty())
|
||||||
|
mKeyboardNavigation->restoreFocus(mGuiModes.back());
|
||||||
|
|
||||||
|
updateVisible();
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::cycleSpell(bool next)
|
void WindowManager::cycleSpell(bool next)
|
||||||
{
|
{
|
||||||
if (!isGuiMode())
|
if (!isGuiMode())
|
||||||
|
@ -123,6 +123,7 @@ namespace MWGui
|
|||||||
class WindowModal;
|
class WindowModal;
|
||||||
class ScreenFader;
|
class ScreenFader;
|
||||||
class DebugWindow;
|
class DebugWindow;
|
||||||
|
class PostProcessorHud;
|
||||||
class JailScreen;
|
class JailScreen;
|
||||||
class KeyboardNavigation;
|
class KeyboardNavigation;
|
||||||
|
|
||||||
@ -188,6 +189,7 @@ namespace MWGui
|
|||||||
MWGui::ConfirmationDialog* getConfirmationDialog() override;
|
MWGui::ConfirmationDialog* getConfirmationDialog() override;
|
||||||
MWGui::TradeWindow* getTradeWindow() override;
|
MWGui::TradeWindow* getTradeWindow() override;
|
||||||
const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() override;
|
const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() override;
|
||||||
|
MWGui::PostProcessorHud* getPostProcessorHud() override;
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false) override;
|
void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false) override;
|
||||||
@ -366,6 +368,7 @@ namespace MWGui
|
|||||||
|
|
||||||
void toggleConsole() override;
|
void toggleConsole() override;
|
||||||
void toggleDebugWindow() override;
|
void toggleDebugWindow() override;
|
||||||
|
void togglePostProcessorHud() override;
|
||||||
|
|
||||||
/// Cycle to next or previous spell
|
/// Cycle to next or previous spell
|
||||||
void cycleSpell(bool next) override;
|
void cycleSpell(bool next) override;
|
||||||
@ -452,6 +455,7 @@ namespace MWGui
|
|||||||
ScreenFader* mHitFader;
|
ScreenFader* mHitFader;
|
||||||
ScreenFader* mScreenFader;
|
ScreenFader* mScreenFader;
|
||||||
DebugWindow* mDebugWindow;
|
DebugWindow* mDebugWindow;
|
||||||
|
PostProcessorHud* mPostProcessorHud;
|
||||||
JailScreen* mJailScreen;
|
JailScreen* mJailScreen;
|
||||||
ContainerWindow* mContainerWindow;
|
ContainerWindow* mContainerWindow;
|
||||||
|
|
||||||
|
@ -241,6 +241,9 @@ namespace MWInput
|
|||||||
case A_ToggleDebug:
|
case A_ToggleDebug:
|
||||||
windowManager->toggleDebugWindow();
|
windowManager->toggleDebugWindow();
|
||||||
break;
|
break;
|
||||||
|
case A_TogglePostProcessorHUD:
|
||||||
|
windowManager->togglePostProcessorHud();
|
||||||
|
break;
|
||||||
case A_QuickSave:
|
case A_QuickSave:
|
||||||
quickSave();
|
quickSave();
|
||||||
break;
|
break;
|
||||||
|
@ -73,6 +73,8 @@ namespace MWInput
|
|||||||
A_ZoomIn,
|
A_ZoomIn,
|
||||||
A_ZoomOut,
|
A_ZoomOut,
|
||||||
|
|
||||||
|
A_TogglePostProcessorHUD,
|
||||||
|
|
||||||
A_Last // Marker for the last item
|
A_Last // Marker for the last item
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,7 @@ namespace MWInput
|
|||||||
defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK;
|
defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK;
|
||||||
defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5;
|
defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5;
|
||||||
defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9;
|
defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9;
|
||||||
|
defaultKeyBindings[A_TogglePostProcessorHUD] = SDL_SCANCODE_F2;
|
||||||
|
|
||||||
std::map<int, int> defaultMouseButtonBindings;
|
std::map<int, int> defaultMouseButtonBindings;
|
||||||
defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT;
|
defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT;
|
||||||
@ -502,6 +503,8 @@ namespace MWInput
|
|||||||
return "#{sQuickSaveCmd}";
|
return "#{sQuickSaveCmd}";
|
||||||
case A_QuickLoad:
|
case A_QuickLoad:
|
||||||
return "#{sQuickLoadCmd}";
|
return "#{sQuickLoadCmd}";
|
||||||
|
case A_TogglePostProcessorHUD:
|
||||||
|
return "Toggle Post Processor HUD";
|
||||||
default:
|
default:
|
||||||
return std::string(); // not configurable
|
return std::string(); // not configurable
|
||||||
}
|
}
|
||||||
@ -563,7 +566,8 @@ namespace MWInput
|
|||||||
A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight, A_AutoMove,
|
A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight, A_AutoMove,
|
||||||
A_Jump, A_Inventory, A_Journal, A_Rest, A_Console, A_QuickSave, A_QuickLoad,
|
A_Jump, A_Inventory, A_Journal, A_Rest, A_Console, A_QuickSave, A_QuickLoad,
|
||||||
A_ToggleHUD, A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3,
|
A_ToggleHUD, A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3,
|
||||||
A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10
|
A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,
|
||||||
|
A_TogglePostProcessorHUD
|
||||||
};
|
};
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -136,6 +136,7 @@ namespace MWLua
|
|||||||
|
|
||||||
{"ToggleHUD", MWInput::A_ToggleHUD},
|
{"ToggleHUD", MWInput::A_ToggleHUD},
|
||||||
{"ToggleDebug", MWInput::A_ToggleDebug},
|
{"ToggleDebug", MWInput::A_ToggleDebug},
|
||||||
|
{"TogglePostProcessorHUD", MWInput::A_TogglePostProcessorHUD},
|
||||||
|
|
||||||
{"ZoomIn", MWInput::A_ZoomIn},
|
{"ZoomIn", MWInput::A_ZoomIn},
|
||||||
{"ZoomOut", MWInput::A_ZoomOut}
|
{"ZoomOut", MWInput::A_ZoomOut}
|
||||||
|
@ -21,6 +21,7 @@ namespace MWLua
|
|||||||
|
|
||||||
sol::table initCorePackage(const Context&);
|
sol::table initCorePackage(const Context&);
|
||||||
sol::table initWorldPackage(const Context&);
|
sol::table initWorldPackage(const Context&);
|
||||||
|
sol::table initPostprocessingPackage(const Context&);
|
||||||
|
|
||||||
sol::table initGlobalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
|
sol::table initGlobalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
|
||||||
sol::table initLocalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
|
sol::table initLocalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
|
||||||
|
@ -95,6 +95,7 @@ namespace MWLua
|
|||||||
mPlayerSettingsPackage = initPlayerSettingsPackage(localContext);
|
mPlayerSettingsPackage = initPlayerSettingsPackage(localContext);
|
||||||
mLocalStoragePackage = initLocalStoragePackage(localContext, &mGlobalStorage);
|
mLocalStoragePackage = initLocalStoragePackage(localContext, &mGlobalStorage);
|
||||||
mPlayerStoragePackage = initPlayerStoragePackage(localContext, &mGlobalStorage, &mPlayerStorage);
|
mPlayerStoragePackage = initPlayerStoragePackage(localContext, &mGlobalStorage, &mPlayerStorage);
|
||||||
|
mPostprocessingPackage = initPostprocessingPackage(localContext);
|
||||||
|
|
||||||
initConfiguration();
|
initConfiguration();
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
@ -407,6 +408,7 @@ namespace MWLua
|
|||||||
scripts->addPackage("openmw.input", mInputPackage);
|
scripts->addPackage("openmw.input", mInputPackage);
|
||||||
scripts->addPackage("openmw.settings", mPlayerSettingsPackage);
|
scripts->addPackage("openmw.settings", mPlayerSettingsPackage);
|
||||||
scripts->addPackage("openmw.storage", mPlayerStoragePackage);
|
scripts->addPackage("openmw.storage", mPlayerStoragePackage);
|
||||||
|
scripts->addPackage("openmw.postprocessing", mPostprocessingPackage);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -140,6 +140,7 @@ namespace MWLua
|
|||||||
sol::table mPlayerSettingsPackage;
|
sol::table mPlayerSettingsPackage;
|
||||||
sol::table mLocalStoragePackage;
|
sol::table mLocalStoragePackage;
|
||||||
sol::table mPlayerStoragePackage;
|
sol::table mPlayerStoragePackage;
|
||||||
|
sol::table mPostprocessingPackage;
|
||||||
|
|
||||||
GlobalScripts mGlobalScripts{&mLua};
|
GlobalScripts mGlobalScripts{&mLua};
|
||||||
std::set<LocalScripts*> mActiveLocalScripts;
|
std::set<LocalScripts*> mActiveLocalScripts;
|
||||||
|
140
apps/openmw/mwlua/postprocessingbindings.cpp
Normal file
140
apps/openmw/mwlua/postprocessingbindings.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <class T>
|
||||||
|
class SetUniformShaderAction final : public MWLua::LuaManager::Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SetUniformShaderAction(LuaUtil::LuaState* state, std::shared_ptr<fx::Technique> shader, const std::string& name, const T& value)
|
||||||
|
: MWLua::LuaManager::Action(state), mShader(std::move(shader)), mName(name), mValue(value) {}
|
||||||
|
|
||||||
|
void apply(MWLua::WorldView&) const override
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(mShader, mName, mValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString() const override
|
||||||
|
{
|
||||||
|
return std::string("SetUniformShaderAction shader=") + (mShader ? mShader->getName() : "nil") +
|
||||||
|
std::string("uniform=") + (mShader ? mName : "nil");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<fx::Technique> mShader;
|
||||||
|
std::string mName;
|
||||||
|
T mValue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct Shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::Shader> : std::false_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct Shader
|
||||||
|
{
|
||||||
|
std::shared_ptr<fx::Technique> mShader;
|
||||||
|
|
||||||
|
Shader(std::shared_ptr<fx::Technique> shader) : mShader(std::move(shader)) {}
|
||||||
|
|
||||||
|
std::string toString() const
|
||||||
|
{
|
||||||
|
if (!mShader)
|
||||||
|
return "Shader(nil)";
|
||||||
|
|
||||||
|
return Misc::StringUtils::format("Shader(%s, %s)", mShader->getName(), mShader->getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mQueuedAction = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
sol::table initPostprocessingPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
|
||||||
|
sol::usertype<Shader> shader = context.mLua->sol().new_usertype<Shader>("Shader");
|
||||||
|
shader[sol::meta_function::to_string] = [](const Shader& shader) { return shader.toString(); };
|
||||||
|
|
||||||
|
shader["enable"] = [context](Shader& shader, sol::optional<int> optPos)
|
||||||
|
{
|
||||||
|
std::optional<int> pos = std::nullopt;
|
||||||
|
if (optPos)
|
||||||
|
pos = optPos.value();
|
||||||
|
|
||||||
|
if (shader.mShader && shader.mShader->isValid())
|
||||||
|
shader.mQueuedAction = true;
|
||||||
|
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[&] { MWBase::Environment::get().getWorld()->getPostProcessor()->enableTechnique(shader.mShader, pos); },
|
||||||
|
"Enable shader " + (shader.mShader ? shader.mShader->getName() : "nil")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["disable"] = [context](Shader& shader)
|
||||||
|
{
|
||||||
|
shader.mQueuedAction = false;
|
||||||
|
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[&] { MWBase::Environment::get().getWorld()->getPostProcessor()->disableTechnique(shader.mShader); },
|
||||||
|
"Disable shader " + (shader.mShader ? shader.mShader->getName() : "nil")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["isEnabled"] = [](const Shader& shader)
|
||||||
|
{
|
||||||
|
return shader.mQueuedAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setBool"] = [context](const Shader& shader, const std::string& name, bool value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<bool>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setFloat"] = [context](const Shader& shader, const std::string& name, float value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<float>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setInt"] = [context](const Shader& shader, const std::string& name, int value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<int>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setVector2"] = [context](const Shader& shader, const std::string& name, const osg::Vec2f& value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<osg::Vec2f>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setVector3"] = [context](const Shader& shader, const std::string& name, const osg::Vec3f& value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<osg::Vec3f>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
shader["setVector4"] = [context](const Shader& shader, const std::string& name, const osg::Vec4f& value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction(std::make_unique<SetUniformShaderAction<osg::Vec4f>>(context.mLua, shader.mShader, name, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
api["load"] = [](const std::string& name)
|
||||||
|
{
|
||||||
|
return Shader(MWBase::Environment::get().getWorld()->getPostProcessor()->loadTechnique(name, false));
|
||||||
|
};
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
#include <osg/Switch>
|
#include <osg/Switch>
|
||||||
#include <osg/LightModel>
|
#include <osg/LightModel>
|
||||||
|
#include <osg/ColorMaski>
|
||||||
|
|
||||||
#include <osgParticle/ParticleSystem>
|
#include <osgParticle/ParticleSystem>
|
||||||
#include <osgParticle/ParticleProcessor>
|
#include <osgParticle/ParticleProcessor>
|
||||||
@ -1563,7 +1564,8 @@ namespace MWRender
|
|||||||
|
|
||||||
// Morrowind has a white ambient light attached to the root VFX node of the scenegraph
|
// Morrowind has a white ambient light attached to the root VFX node of the scenegraph
|
||||||
node->getOrCreateStateSet()->setAttributeAndModes(getVFXLightModelInstance(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
node->getOrCreateStateSet()->setAttributeAndModes(getVFXLightModelInstance(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||||
|
if (mResourceSystem->getSceneManager()->getSupportsNormalsRT())
|
||||||
|
node->getOrCreateStateSet()->setAttribute(new osg::ColorMaski(1, false, false, false, false));
|
||||||
SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor;
|
SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor;
|
||||||
node->accept(findMaxLengthVisitor);
|
node->accept(findMaxLengthVisitor);
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ namespace MWRender
|
|||||||
class CharacterPreviewRTTNode : public SceneUtil::RTTNode
|
class CharacterPreviewRTTNode : public SceneUtil::RTTNode
|
||||||
{
|
{
|
||||||
static constexpr float fovYDegrees = 12.3f;
|
static constexpr float fovYDegrees = 12.3f;
|
||||||
static constexpr float znear = 0.1f;
|
static constexpr float znear = 4.0f;
|
||||||
static constexpr float zfar = 10000.f;
|
static constexpr float zfar = 10000.f;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -162,31 +162,23 @@ namespace MWRender
|
|||||||
mGroup->getOrCreateStateSet()->addUniform(new osg::Uniform("projectionMatrix", mPerspectiveMatrix));
|
mGroup->getOrCreateStateSet()->addUniform(new osg::Uniform("projectionMatrix", mPerspectiveMatrix));
|
||||||
mViewMatrix = osg::Matrixf::identity();
|
mViewMatrix = osg::Matrixf::identity();
|
||||||
setColorBufferInternalFormat(GL_RGBA);
|
setColorBufferInternalFormat(GL_RGBA);
|
||||||
|
setDepthBufferInternalFormat(GL_DEPTH24_STENCIL8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDefaults(osg::Camera* camera) override
|
void setDefaults(osg::Camera* camera) override
|
||||||
{
|
{
|
||||||
|
|
||||||
// hints that the camera is not relative to the master camera
|
|
||||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
|
||||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
|
||||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
|
||||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
camera->setViewport(0, 0, width(), height());
|
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
|
||||||
camera->setName("CharacterPreview");
|
camera->setName("CharacterPreview");
|
||||||
camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
|
||||||
camera->setCullMask(~(Mask_UpdateVisitor));
|
|
||||||
SceneUtil::setCameraClearDepth(camera);
|
|
||||||
|
|
||||||
// hints that the camera is not relative to the master camera
|
|
||||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
||||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
camera->setProjectionMatrixAsPerspective(fovYDegrees, mAspectRatio, znear, zfar);
|
camera->setProjectionMatrixAsPerspective(fovYDegrees, mAspectRatio, znear, zfar);
|
||||||
camera->setViewport(0, 0, width(), height());
|
camera->setViewport(0, 0, width(), height());
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||||
|
camera->setCullMask(~(Mask_UpdateVisitor));
|
||||||
|
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||||
|
SceneUtil::setCameraClearDepth(camera);
|
||||||
|
|
||||||
#ifdef OSG_HAS_MULTIVIEW
|
#ifdef OSG_HAS_MULTIVIEW
|
||||||
if (shouldDoTextureArray())
|
if (shouldDoTextureArray())
|
||||||
{
|
{
|
||||||
|
123
apps/openmw/mwrender/hdr.cpp
Normal file
123
apps/openmw/mwrender/hdr.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#include "hdr.hpp"
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
|
#include "pingpongcanvas.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
HDRDriver::HDRDriver(Shader::ShaderManager& shaderManager)
|
||||||
|
: mCompiled(false)
|
||||||
|
, mEnabled(false)
|
||||||
|
, mWidth(1)
|
||||||
|
, mHeight(1)
|
||||||
|
{
|
||||||
|
const float hdrExposureTime = std::clamp(Settings::Manager::getFloat("hdr exposure time", "Post Processing"), 0.f, 1.f);
|
||||||
|
|
||||||
|
constexpr float minLog = -9.0;
|
||||||
|
constexpr float maxLog = 4.0;
|
||||||
|
constexpr float logLumRange = (maxLog - minLog);
|
||||||
|
constexpr float invLogLumRange = 1.0 / logLumRange;
|
||||||
|
constexpr float epsilon = 0.004;
|
||||||
|
|
||||||
|
Shader::ShaderManager::DefineMap defines = {
|
||||||
|
{"minLog", std::to_string(minLog)},
|
||||||
|
{"maxLog", std::to_string(maxLog)},
|
||||||
|
{"logLumRange", std::to_string(logLumRange)},
|
||||||
|
{"invLogLumRange", std::to_string(invLogLumRange)},
|
||||||
|
{"hdrExposureTime", std::to_string(hdrExposureTime)},
|
||||||
|
{"epsilon", std::to_string(epsilon)},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto vertex = shaderManager.getShader("fullscreen_tri_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||||
|
auto hdrLuminance = shaderManager.getShader("hdr_luminance_fragment.glsl", defines, osg::Shader::FRAGMENT);
|
||||||
|
auto hdr = shaderManager.getShader("hdr_fragment.glsl", defines, osg::Shader::FRAGMENT);
|
||||||
|
|
||||||
|
mProgram = shaderManager.getProgram(vertex, hdr);
|
||||||
|
mLuminanceProgram = shaderManager.getProgram(vertex, hdrLuminance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HDRDriver::compile()
|
||||||
|
{
|
||||||
|
int mipmapLevels = osg::Image::computeNumberOfMipmapLevels(mWidth, mHeight);
|
||||||
|
|
||||||
|
for (auto& buffer : mBuffers)
|
||||||
|
{
|
||||||
|
buffer.texture = new osg::Texture2D;
|
||||||
|
buffer.texture->setInternalFormat(GL_R16F);
|
||||||
|
buffer.texture->setSourceFormat(GL_RED);
|
||||||
|
buffer.texture->setSourceType(GL_FLOAT);
|
||||||
|
buffer.texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST);
|
||||||
|
buffer.texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
|
||||||
|
buffer.texture->setTextureSize(mWidth, mHeight);
|
||||||
|
buffer.texture->setNumMipmapLevels(mipmapLevels);
|
||||||
|
|
||||||
|
buffer.finalTexture = new osg::Texture2D;
|
||||||
|
buffer.finalTexture->setInternalFormat(GL_R16F);
|
||||||
|
buffer.finalTexture->setSourceFormat(GL_RED);
|
||||||
|
buffer.finalTexture->setSourceType(GL_FLOAT);
|
||||||
|
buffer.finalTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
||||||
|
buffer.finalTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
||||||
|
buffer.finalTexture->setTextureSize(1, 1);
|
||||||
|
|
||||||
|
buffer.finalFbo = new osg::FrameBufferObject;
|
||||||
|
buffer.finalFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.finalTexture));
|
||||||
|
|
||||||
|
buffer.fullscreenFbo = new osg::FrameBufferObject;
|
||||||
|
buffer.fullscreenFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.texture));
|
||||||
|
|
||||||
|
buffer.mipmapFbo = new osg::FrameBufferObject;
|
||||||
|
buffer.mipmapFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.texture, mipmapLevels - 1));
|
||||||
|
|
||||||
|
buffer.fullscreenStateset = new osg::StateSet;
|
||||||
|
buffer.fullscreenStateset->setAttributeAndModes(mLuminanceProgram);
|
||||||
|
buffer.fullscreenStateset->addUniform(new osg::Uniform("sceneTex", 0));
|
||||||
|
|
||||||
|
buffer.mipmapStateset = new osg::StateSet;
|
||||||
|
buffer.mipmapStateset->setAttributeAndModes(mProgram);
|
||||||
|
buffer.mipmapStateset->setTextureAttributeAndModes(0, buffer.texture);
|
||||||
|
buffer.mipmapStateset->addUniform(new osg::Uniform("luminanceSceneTex", 0));
|
||||||
|
buffer.mipmapStateset->addUniform(new osg::Uniform("prevLuminanceSceneTex", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuffers[0].mipmapStateset->setTextureAttributeAndModes(1, mBuffers[1].finalTexture);
|
||||||
|
mBuffers[1].mipmapStateset->setTextureAttributeAndModes(1, mBuffers[0].finalTexture);
|
||||||
|
|
||||||
|
mCompiled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HDRDriver::draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId)
|
||||||
|
{
|
||||||
|
if (!mEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!mCompiled)
|
||||||
|
compile();
|
||||||
|
|
||||||
|
auto& hdrBuffer = mBuffers[frameId];
|
||||||
|
hdrBuffer.fullscreenFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
hdrBuffer.fullscreenStateset->setTextureAttributeAndModes(0, canvas.getSceneTexture(frameId));
|
||||||
|
|
||||||
|
state.apply(hdrBuffer.fullscreenStateset);
|
||||||
|
canvas.drawGeometry(renderInfo);
|
||||||
|
|
||||||
|
state.applyTextureAttribute(0, hdrBuffer.texture);
|
||||||
|
ext->glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
hdrBuffer.mipmapFbo->apply(state, osg::FrameBufferObject::READ_FRAMEBUFFER);
|
||||||
|
hdrBuffer.finalFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
|
|
||||||
|
state.apply(hdrBuffer.mipmapStateset);
|
||||||
|
canvas.drawGeometry(renderInfo);
|
||||||
|
|
||||||
|
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> HDRDriver::getLuminanceTexture(size_t frameId) const
|
||||||
|
{
|
||||||
|
return mBuffers[frameId].finalTexture;
|
||||||
|
}
|
||||||
|
}
|
71
apps/openmw/mwrender/hdr.hpp
Normal file
71
apps/openmw/mwrender/hdr.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef OPENMW_MWRENDER_HDR_H
|
||||||
|
#define OPENMW_MWRENDER_HDR_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <osg/FrameBufferObject>
|
||||||
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/Program>
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
class ShaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
class PingPongCanvas;
|
||||||
|
|
||||||
|
class HDRDriver
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
HDRDriver() = default;
|
||||||
|
|
||||||
|
HDRDriver(Shader::ShaderManager& shaderManager);
|
||||||
|
|
||||||
|
void draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId);
|
||||||
|
|
||||||
|
bool isEnabled() const { return mEnabled; }
|
||||||
|
|
||||||
|
void enable() { mEnabled = true; }
|
||||||
|
void disable() { mEnabled = false; }
|
||||||
|
|
||||||
|
void dirty(int w, int h)
|
||||||
|
{
|
||||||
|
mWidth = w;
|
||||||
|
mHeight = h;
|
||||||
|
mCompiled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> getLuminanceTexture(size_t frameId) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void compile();
|
||||||
|
|
||||||
|
struct HDRContainer
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> fullscreenFbo;
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> mipmapFbo;
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> finalFbo;
|
||||||
|
osg::ref_ptr<osg::Texture2D> texture;
|
||||||
|
osg::ref_ptr<osg::Texture2D> finalTexture;
|
||||||
|
osg::ref_ptr<osg::StateSet> fullscreenStateset;
|
||||||
|
osg::ref_ptr<osg::StateSet> mipmapStateset;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<HDRContainer, 2> mBuffers;
|
||||||
|
osg::ref_ptr<osg::Program> mLuminanceProgram;
|
||||||
|
osg::ref_ptr<osg::Program> mProgram;
|
||||||
|
|
||||||
|
bool mCompiled;
|
||||||
|
bool mEnabled;
|
||||||
|
|
||||||
|
int mWidth;
|
||||||
|
int mHeight;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -670,6 +670,7 @@ LocalMapRenderToTexture::LocalMapRenderToTexture(osg::Node* sceneRoot, int res,
|
|||||||
mViewMatrix.makeLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
|
mViewMatrix.makeLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
|
||||||
|
|
||||||
setUpdateCallback(new CameraLocalUpdateCallback);
|
setUpdateCallback(new CameraLocalUpdateCallback);
|
||||||
|
setDepthBufferInternalFormat(GL_DEPTH24_STENCIL8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalMapRenderToTexture::setDefaults(osg::Camera* camera)
|
void LocalMapRenderToTexture::setDefaults(osg::Camera* camera)
|
||||||
|
@ -327,24 +327,31 @@ public:
|
|||||||
|
|
||||||
state->applyAttribute(mDepth);
|
state->applyAttribute(mDepth);
|
||||||
|
|
||||||
if (postProcessor && postProcessor->getFirstPersonRBProxy())
|
unsigned int frameId = state->getFrameStamp()->getFrameNumber() % 2;
|
||||||
{
|
|
||||||
osg::GLExtensions* ext = state->get<osg::GLExtensions>();
|
|
||||||
|
|
||||||
osg::FrameBufferAttachment(postProcessor->getFirstPersonRBProxy()).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext);
|
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId))
|
||||||
|
{
|
||||||
|
postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state);
|
||||||
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
// color accumulation pass
|
// color accumulation pass
|
||||||
bin->drawImplementation(renderInfo, previous);
|
bin->drawImplementation(renderInfo, previous);
|
||||||
|
|
||||||
auto primaryFBO = postProcessor->getMsaaFbo() ? postProcessor->getMsaaFbo() : postProcessor->getFbo();
|
auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
|
||||||
primaryFBO->getAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext);
|
|
||||||
|
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
||||||
|
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
|
||||||
|
else
|
||||||
|
primaryFBO->apply(*state);
|
||||||
|
|
||||||
state->pushStateSet(mStateSet);
|
|
||||||
state->apply();
|
|
||||||
// depth accumulation pass
|
// depth accumulation pass
|
||||||
|
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
|
||||||
|
bin->setStateSet(mStateSet);
|
||||||
bin->drawImplementation(renderInfo, previous);
|
bin->drawImplementation(renderInfo, previous);
|
||||||
state->popStateSet();
|
bin->setStateSet(restore);
|
||||||
|
|
||||||
|
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
||||||
|
primaryFBO->apply(*state);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
260
apps/openmw/mwrender/pingpongcanvas.cpp
Normal file
260
apps/openmw/mwrender/pingpongcanvas.cpp
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
#include "pingpongcanvas.hpp"
|
||||||
|
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include "postprocessor.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
PingPongCanvas::PingPongCanvas(Shader::ShaderManager& shaderManager)
|
||||||
|
: mFallbackStateSet(new osg::StateSet)
|
||||||
|
, mQueuedDispatchArray(std::nullopt)
|
||||||
|
, mQueuedDispatchFrameId(0)
|
||||||
|
{
|
||||||
|
setUseDisplayList(false);
|
||||||
|
setUseVertexBufferObjects(true);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array;
|
||||||
|
verts->push_back(osg::Vec3f(-1, -1, 0));
|
||||||
|
verts->push_back(osg::Vec3f(-1, 3, 0));
|
||||||
|
verts->push_back(osg::Vec3f(3, -1, 0));
|
||||||
|
|
||||||
|
setVertexArray(verts);
|
||||||
|
|
||||||
|
addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 3));
|
||||||
|
|
||||||
|
mHDRDriver = HDRDriver(shaderManager);
|
||||||
|
mHDRDriver.disable();
|
||||||
|
|
||||||
|
auto fallbackVertex = shaderManager.getShader("fullscreen_tri_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||||
|
auto fallbackFragment = shaderManager.getShader("fullscreen_tri_fragment.glsl", {}, osg::Shader::FRAGMENT);
|
||||||
|
mFallbackProgram = shaderManager.getProgram(fallbackVertex, fallbackFragment);
|
||||||
|
|
||||||
|
mFallbackStateSet->setAttributeAndModes(mFallbackProgram);
|
||||||
|
mFallbackStateSet->addUniform(new osg::Uniform("omw_SamplerLastShader", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PingPongCanvas::setCurrentFrameData(size_t frameId, fx::DispatchArray&& data)
|
||||||
|
{
|
||||||
|
mQueuedDispatchArray = fx::DispatchArray(data);
|
||||||
|
mQueuedDispatchFrameId = !frameId;
|
||||||
|
|
||||||
|
mBufferData[frameId].data = std::move(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PingPongCanvas::setMask(size_t frameId, bool underwater, bool exterior)
|
||||||
|
{
|
||||||
|
mBufferData[frameId].mask = 0;
|
||||||
|
|
||||||
|
mBufferData[frameId].mask |= underwater ? fx::Technique::Flag_Disable_Underwater : fx::Technique::Flag_Disable_Abovewater;
|
||||||
|
mBufferData[frameId].mask |= exterior ? fx::Technique::Flag_Disable_Exteriors : fx::Technique::Flag_Disable_Interiors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PingPongCanvas::drawGeometry(osg::RenderInfo& renderInfo) const
|
||||||
|
{
|
||||||
|
osg::Geometry::drawImplementation(renderInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PingPongCanvas::drawImplementation(osg::RenderInfo& renderInfo) const
|
||||||
|
{
|
||||||
|
osg::State& state = *renderInfo.getState();
|
||||||
|
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
|
||||||
|
|
||||||
|
size_t frameId = state.getFrameStamp()->getFrameNumber() % 2;
|
||||||
|
|
||||||
|
auto& bufferData = mBufferData[frameId];
|
||||||
|
|
||||||
|
if (mQueuedDispatchArray && mQueuedDispatchFrameId == frameId)
|
||||||
|
{
|
||||||
|
mBufferData[frameId].data = std::move(mQueuedDispatchArray.value());
|
||||||
|
mQueuedDispatchArray = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& data = bufferData.data;
|
||||||
|
|
||||||
|
std::vector<size_t> filtered;
|
||||||
|
|
||||||
|
filtered.reserve(data.size());
|
||||||
|
|
||||||
|
const fx::DispatchNode::SubPass* resolvePass = nullptr;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto& node = data[i];
|
||||||
|
|
||||||
|
if (bufferData.mask & node.mFlags)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (auto it = node.mPasses.crbegin(); it != node.mPasses.crend(); ++it)
|
||||||
|
{
|
||||||
|
if (!(*it).mRenderTarget)
|
||||||
|
{
|
||||||
|
resolvePass = &(*it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* viewport = state.getCurrentViewport();
|
||||||
|
|
||||||
|
if (filtered.empty() || !bufferData.postprocessing)
|
||||||
|
{
|
||||||
|
if (bufferData.postprocessing)
|
||||||
|
Log(Debug::Error) << "Critical error, postprocess shaders failed to compile. Using default shader.";
|
||||||
|
|
||||||
|
mFallbackStateSet->setTextureAttributeAndModes(0, bufferData.sceneTex);
|
||||||
|
|
||||||
|
state.pushStateSet(mFallbackStateSet);
|
||||||
|
state.apply();
|
||||||
|
viewport->apply(state);
|
||||||
|
|
||||||
|
drawGeometry(renderInfo);
|
||||||
|
state.popStateSet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int handle = mFbos[0] ? mFbos[0]->getHandle(state.getContextID()) : 0;
|
||||||
|
|
||||||
|
if (handle == 0 || bufferData.dirty)
|
||||||
|
{
|
||||||
|
for (auto& fbo : mFbos)
|
||||||
|
{
|
||||||
|
fbo = new osg::FrameBufferObject;
|
||||||
|
fbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(new osg::Texture2D(*bufferData.sceneTexLDR)));
|
||||||
|
fbo->apply(state);
|
||||||
|
glClearColor(0.5, 0.5, 0.5, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
mHDRDriver.dirty(bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight());
|
||||||
|
|
||||||
|
bufferData.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::array<std::array<int, 2>, 3> buffers = {{
|
||||||
|
{GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT},
|
||||||
|
{GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT},
|
||||||
|
{GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT}
|
||||||
|
}};
|
||||||
|
|
||||||
|
(bufferData.hdr) ? mHDRDriver.enable() : mHDRDriver.disable();
|
||||||
|
|
||||||
|
// A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly supported, so that's what we use for now.
|
||||||
|
mHDRDriver.draw(*this, renderInfo, state, ext, frameId);
|
||||||
|
|
||||||
|
auto buffer = buffers[0];
|
||||||
|
|
||||||
|
int lastDraw = 0;
|
||||||
|
int lastShader = 0;
|
||||||
|
|
||||||
|
unsigned int lastApplied = handle;
|
||||||
|
|
||||||
|
const unsigned int cid = state.getContextID();
|
||||||
|
|
||||||
|
const osg::ref_ptr<osg::FrameBufferObject>& destinationFbo = bufferData.destination ? bufferData.destination : nullptr;
|
||||||
|
unsigned int destinationHandle = destinationFbo ? destinationFbo->getHandle(cid) : 0;
|
||||||
|
|
||||||
|
auto bindDestinationFbo = [&]() {
|
||||||
|
if (destinationFbo)
|
||||||
|
{
|
||||||
|
destinationFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
lastApplied = destinationHandle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ext->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
|
||||||
|
|
||||||
|
lastApplied = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const size_t& index : filtered)
|
||||||
|
{
|
||||||
|
const auto& node = data[index];
|
||||||
|
|
||||||
|
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, bufferData.depthTex);
|
||||||
|
|
||||||
|
if (bufferData.hdr)
|
||||||
|
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_EyeAdaptation, mHDRDriver.getLuminanceTexture(frameId));
|
||||||
|
|
||||||
|
if (bufferData.normalsTex)
|
||||||
|
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, bufferData.normalsTex);
|
||||||
|
|
||||||
|
state.pushStateSet(node.mRootStateSet);
|
||||||
|
state.apply();
|
||||||
|
|
||||||
|
for (size_t passIndex = 0; passIndex < node.mPasses.size(); ++passIndex)
|
||||||
|
{
|
||||||
|
const auto& pass = node.mPasses[passIndex];
|
||||||
|
|
||||||
|
bool lastPass = passIndex == node.mPasses.size() - 1;
|
||||||
|
|
||||||
|
if (lastShader == 0)
|
||||||
|
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, bufferData.sceneTex);
|
||||||
|
else
|
||||||
|
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, (osg::Texture2D*)mFbos[lastShader - GL_COLOR_ATTACHMENT0_EXT]->getAttachment(osg::Camera::COLOR_BUFFER0).getTexture());
|
||||||
|
|
||||||
|
if (lastDraw == 0)
|
||||||
|
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, bufferData.sceneTex);
|
||||||
|
else
|
||||||
|
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, (osg::Texture2D*)mFbos[lastDraw - GL_COLOR_ATTACHMENT0_EXT]->getAttachment(osg::Camera::COLOR_BUFFER0).getTexture());
|
||||||
|
|
||||||
|
if (pass.mRenderTarget)
|
||||||
|
{
|
||||||
|
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
|
||||||
|
{
|
||||||
|
state.setActiveTextureUnit(0);
|
||||||
|
state.applyTextureAttribute(0, pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0).getTexture());
|
||||||
|
ext->glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastApplied = pass.mRenderTarget->getHandle(state.getContextID());;
|
||||||
|
}
|
||||||
|
else if (&pass == resolvePass)
|
||||||
|
{
|
||||||
|
bindDestinationFbo();
|
||||||
|
}
|
||||||
|
else if (lastPass)
|
||||||
|
{
|
||||||
|
lastDraw = buffer[0];
|
||||||
|
lastShader = buffer[0];
|
||||||
|
mFbos[buffer[0] - GL_COLOR_ATTACHMENT0_EXT]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
buffer = buffers[lastShader - GL_COLOR_ATTACHMENT0_EXT];
|
||||||
|
|
||||||
|
lastApplied = mFbos[buffer[0] - GL_COLOR_ATTACHMENT0_EXT]->getHandle(cid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFbos[buffer[0] - GL_COLOR_ATTACHMENT0_EXT]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
lastDraw = buffer[0];
|
||||||
|
std::swap(buffer[0], buffer[1]);
|
||||||
|
|
||||||
|
lastApplied = mFbos[buffer[0] - GL_COLOR_ATTACHMENT0_EXT]->getHandle(cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.pushStateSet(pass.mStateSet);
|
||||||
|
state.apply();
|
||||||
|
|
||||||
|
if (!state.getLastAppliedProgramObject())
|
||||||
|
mFallbackProgram->apply(state);
|
||||||
|
|
||||||
|
drawGeometry(renderInfo);
|
||||||
|
|
||||||
|
state.popStateSet();
|
||||||
|
state.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.popStateSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastApplied != destinationHandle)
|
||||||
|
{
|
||||||
|
bindDestinationFbo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
apps/openmw/mwrender/pingpongcanvas.hpp
Normal file
88
apps/openmw/mwrender/pingpongcanvas.hpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#ifndef OPENMW_MWRENDER_PINGPONGCANVAS_H
|
||||||
|
#define OPENMW_MWRENDER_PINGPONGCANVAS_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <osg/Geometry>
|
||||||
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/FrameBufferObject>
|
||||||
|
|
||||||
|
#include <components/fx/technique.hpp>
|
||||||
|
|
||||||
|
#include "postprocessor.hpp"
|
||||||
|
#include "hdr.hpp"
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
class ShaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
class PingPongCanvas : public osg::Geometry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PingPongCanvas(Shader::ShaderManager& shaderManager);
|
||||||
|
|
||||||
|
void drawImplementation(osg::RenderInfo& renderInfo) const override;
|
||||||
|
|
||||||
|
void dirty(size_t frameId) { mBufferData[frameId].dirty = true; }
|
||||||
|
|
||||||
|
const fx::DispatchArray& getCurrentFrameData(size_t frame) { return mBufferData[frame % 2].data; }
|
||||||
|
|
||||||
|
// Sets current frame pass data and stores copy of dispatch array to apply to next frame data
|
||||||
|
void setCurrentFrameData(size_t frameId, fx::DispatchArray&& data);
|
||||||
|
|
||||||
|
void setMask(size_t frameId, bool underwater, bool exterior);
|
||||||
|
|
||||||
|
void setSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].sceneTex = tex; }
|
||||||
|
|
||||||
|
void setLDRSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture2D> tex) { mBufferData[frameId].sceneTexLDR = tex; }
|
||||||
|
|
||||||
|
void setDepthTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].depthTex = tex; }
|
||||||
|
|
||||||
|
void setNormalsTexture(size_t frameId, osg::ref_ptr<osg::Texture2D> tex) { mBufferData[frameId].normalsTex = tex; }
|
||||||
|
|
||||||
|
void setHDR(size_t frameId, bool hdr) { mBufferData[frameId].hdr = hdr; }
|
||||||
|
|
||||||
|
void setPostProcessing(size_t frameId, bool postprocessing) { mBufferData[frameId].postprocessing = postprocessing; }
|
||||||
|
|
||||||
|
const osg::ref_ptr<osg::Texture>& getSceneTexture(size_t frameId) const { return mBufferData[frameId].sceneTex; }
|
||||||
|
|
||||||
|
void drawGeometry(osg::RenderInfo& renderInfo) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void copyNewFrameData(size_t frameId) const;
|
||||||
|
|
||||||
|
mutable HDRDriver mHDRDriver;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Program> mFallbackProgram;
|
||||||
|
osg::ref_ptr<osg::StateSet> mFallbackStateSet;
|
||||||
|
|
||||||
|
struct BufferData
|
||||||
|
{
|
||||||
|
bool dirty = false;
|
||||||
|
bool hdr = false;
|
||||||
|
bool postprocessing = true;
|
||||||
|
|
||||||
|
fx::DispatchArray data;
|
||||||
|
fx::FlagsType mask;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> destination;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture> sceneTex;
|
||||||
|
osg::ref_ptr<osg::Texture> depthTex;
|
||||||
|
osg::ref_ptr<osg::Texture2D> sceneTexLDR;
|
||||||
|
osg::ref_ptr<osg::Texture2D> normalsTex;
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable std::array<BufferData, 2> mBufferData;
|
||||||
|
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
|
||||||
|
|
||||||
|
mutable std::optional<fx::DispatchArray> mQueuedDispatchArray;
|
||||||
|
mutable size_t mQueuedDispatchFrameId;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
47
apps/openmw/mwrender/pingpongcull.cpp
Normal file
47
apps/openmw/mwrender/pingpongcull.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "pingpongcull.hpp"
|
||||||
|
|
||||||
|
#include <osg/Camera>
|
||||||
|
#include <osg/FrameBufferObject>
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
|
#include "postprocessor.hpp"
|
||||||
|
#include "pingpongcanvas.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
void PingPongCull::operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||||
|
{
|
||||||
|
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
|
||||||
|
size_t frame = cv->getTraversalNumber();
|
||||||
|
size_t frameId = frame % 2;
|
||||||
|
|
||||||
|
MWRender::PostProcessor* postProcessor = dynamic_cast<MWRender::PostProcessor*>(cv->getCurrentCamera()->getUserData());
|
||||||
|
|
||||||
|
postProcessor->getStateUpdater()->setViewMatrix(cv->getCurrentCamera()->getViewMatrix());
|
||||||
|
postProcessor->getStateUpdater()->setInvViewMatrix(cv->getCurrentCamera()->getInverseViewMatrix());
|
||||||
|
postProcessor->getStateUpdater()->setPrevViewMatrix(mLastViewMatrix);
|
||||||
|
mLastViewMatrix = cv->getCurrentCamera()->getViewMatrix();
|
||||||
|
postProcessor->getStateUpdater()->setEyePos(cv->getEyePoint());
|
||||||
|
postProcessor->getStateUpdater()->setEyeVec(cv->getLookVectorLocal());
|
||||||
|
|
||||||
|
if (!postProcessor || !postProcessor->getFbo(PostProcessor::FBO_Primary, frameId))
|
||||||
|
{
|
||||||
|
renderStage->setMultisampleResolveFramebufferObject(nullptr);
|
||||||
|
renderStage->setFrameBufferObject(nullptr);
|
||||||
|
traverse(node, cv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId))
|
||||||
|
{
|
||||||
|
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderStage->setMultisampleResolveFramebufferObject(postProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
|
||||||
|
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId));
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(node, cv);
|
||||||
|
}
|
||||||
|
}
|
22
apps/openmw/mwrender/pingpongcull.hpp
Normal file
22
apps/openmw/mwrender/pingpongcull.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef OPENMW_MWRENDER_PINGPONGCULL_H
|
||||||
|
#define OPENMW_MWRENDER_PINGPONGCULL_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <components/sceneutil/nodecallback.hpp>
|
||||||
|
|
||||||
|
#include "postprocessor.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
class PostProcessor;
|
||||||
|
class PingPongCull : public SceneUtil::NodeCallback<PingPongCull, osg::Node*, osgUtil::CullVisitor*>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void operator()(osg::Node* node, osgUtil::CullVisitor* nv);
|
||||||
|
private:
|
||||||
|
osg::Matrixf mLastViewMatrix;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,26 @@
|
|||||||
#ifndef OPENMW_MWRENDER_POSTPROCESSOR_H
|
#ifndef OPENMW_MWRENDER_POSTPROCESSOR_H
|
||||||
#define OPENMW_MWRENDER_POSTPROCESSOR_H
|
#define OPENMW_MWRENDER_POSTPROCESSOR_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/FrameBufferObject>
|
#include <osg/FrameBufferObject>
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/ref_ptr>
|
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
|
#include <components/fx/stateupdater.hpp>
|
||||||
|
#include <components/fx/technique.hpp>
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include "pingpongcanvas.hpp"
|
||||||
|
#include "transparentpass.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -19,38 +34,197 @@ namespace Stereo
|
|||||||
class MultiviewFramebuffer;
|
class MultiviewFramebuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace VFS
|
||||||
|
{
|
||||||
|
class Manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
class ShaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class PostProcessor : public osg::Referenced
|
class RenderingManager;
|
||||||
|
class PingPongCull;
|
||||||
|
class PingPongCanvas;
|
||||||
|
class TransparentDepthBinCallback;
|
||||||
|
|
||||||
|
class PostProcessor : public osg::Group
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode);
|
using FBOArray = std::array<osg::ref_ptr<osg::FrameBufferObject>, 5>;
|
||||||
|
using TextureArray = std::array<osg::ref_ptr<osg::Texture2D>, 5>;
|
||||||
|
using TechniqueList = std::vector<std::shared_ptr<fx::Technique>>;
|
||||||
|
|
||||||
auto getMsaaFbo() { return mMsaaFbo; }
|
enum TextureIndex
|
||||||
auto getFbo() { return mFbo; }
|
{
|
||||||
auto getFirstPersonRBProxy() { return mFirstPersonDepthRBProxy; }
|
Tex_Scene,
|
||||||
|
Tex_Scene_LDR,
|
||||||
|
Tex_Depth,
|
||||||
|
Tex_OpaqueDepth,
|
||||||
|
Tex_Normal
|
||||||
|
};
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> getOpaqueDepthTex() { return mOpaqueDepthTex; }
|
enum FBOIndex
|
||||||
|
{
|
||||||
|
FBO_Primary,
|
||||||
|
FBO_Multisample,
|
||||||
|
FBO_FirstPerson,
|
||||||
|
FBO_OpaqueDepth,
|
||||||
|
FBO_Intercept
|
||||||
|
};
|
||||||
|
|
||||||
void resize(int width, int height);
|
enum TextureUnits
|
||||||
|
{
|
||||||
|
Unit_LastShader = 0,
|
||||||
|
Unit_LastPass,
|
||||||
|
Unit_Depth,
|
||||||
|
Unit_EyeAdaptation,
|
||||||
|
Unit_Normals,
|
||||||
|
Unit_NextFree
|
||||||
|
};
|
||||||
|
|
||||||
|
PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs);
|
||||||
|
|
||||||
|
~PostProcessor();
|
||||||
|
|
||||||
|
void traverse(osg::NodeVisitor& nv) override;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> getFbo(FBOIndex index, unsigned int frameId) { return mFbos[frameId][index]; }
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> getTexture(TextureIndex index, unsigned int frameId) { return mTextures[frameId][index]; }
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> getPrimaryFbo(unsigned int frameId) { return mFbos[frameId][FBO_Multisample] ? mFbos[frameId][FBO_Multisample] : mFbos[frameId][FBO_Primary]; }
|
||||||
|
|
||||||
|
osg::ref_ptr<fx::StateUpdater> getStateUpdater() { return mStateUpdater; }
|
||||||
|
|
||||||
|
const TechniqueList& getTechniques() { return mTechniques; }
|
||||||
|
|
||||||
|
const TechniqueList& getTemplates() const { return mTemplates; }
|
||||||
|
|
||||||
|
osg::ref_ptr<PingPongCanvas> getCanvas() { return mPingPongCanvas; }
|
||||||
|
|
||||||
|
const auto& getTechniqueMap() const { return mTechniqueFileMap; }
|
||||||
|
|
||||||
|
void resize();
|
||||||
|
|
||||||
|
bool enableTechnique(std::shared_ptr<fx::Technique> technique, std::optional<int> location = std::nullopt);
|
||||||
|
|
||||||
|
bool disableTechnique(std::shared_ptr<fx::Technique> technique, bool dirty = true);
|
||||||
|
|
||||||
|
bool getSupportsNormalsRT() const { return mNormalsSupported; }
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void setUniform(std::shared_ptr<fx::Technique> technique, const std::string& name, const T& value)
|
||||||
|
{
|
||||||
|
if (!isEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = technique->findUniform(name);
|
||||||
|
|
||||||
|
if (it == technique->getUniformMap().end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((*it)->mStatic)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Attempting to set a configration variable [" << name << "] as a uniform";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*it)->setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTechniqueEnabled(const std::shared_ptr<fx::Technique>& technique) const;
|
||||||
|
|
||||||
|
void setExteriorFlag(bool exterior) { mExteriorFlag = exterior; }
|
||||||
|
|
||||||
|
void setUnderwaterFlag(bool underwater) { mUnderwater = underwater; }
|
||||||
|
|
||||||
|
void toggleMode();
|
||||||
|
|
||||||
|
std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool insert=true);
|
||||||
|
|
||||||
|
void addTemplate(std::shared_ptr<fx::Technique> technique);
|
||||||
|
|
||||||
|
bool isEnabled() const { return mUsePostProcessing && mEnabled; }
|
||||||
|
|
||||||
|
bool softParticlesEnabled() const {return mSoftParticles; }
|
||||||
|
|
||||||
|
bool getHDR() const { return mHDR; }
|
||||||
|
|
||||||
|
void disable();
|
||||||
|
|
||||||
|
void enable(bool usePostProcessing = true);
|
||||||
|
|
||||||
|
void setRenderTargetSize(int width, int height) { mWidth = width; mHeight = height; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createTexturesAndCamera(int width, int height);
|
size_t frame() const { return mViewer->getFrameStamp()->getFrameNumber(); }
|
||||||
|
|
||||||
|
void createObjectsForFrame(size_t frameId);
|
||||||
|
|
||||||
|
void createTexturesAndCamera(size_t frameId);
|
||||||
|
|
||||||
|
void reloadTechniques();
|
||||||
|
|
||||||
|
void reloadMainPass(fx::Technique& technique);
|
||||||
|
|
||||||
|
void dirtyTechniques();
|
||||||
|
|
||||||
|
void update(size_t frameId);
|
||||||
|
|
||||||
|
void cull(size_t frameId, osgUtil::CullVisitor* cv);
|
||||||
|
|
||||||
osgViewer::Viewer* mViewer;
|
|
||||||
osg::ref_ptr<osg::Group> mRootNode;
|
osg::ref_ptr<osg::Group> mRootNode;
|
||||||
osg::ref_ptr<osg::Camera> mHUDCamera;
|
osg::ref_ptr<osg::Camera> mHUDCamera;
|
||||||
|
|
||||||
std::shared_ptr<Stereo::MultiviewFramebuffer> mMultiviewFbo;
|
std::array<TextureArray, 2> mTextures;
|
||||||
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
|
std::array<FBOArray, 2> mFbos;
|
||||||
osg::ref_ptr<osg::FrameBufferObject> mFbo;
|
|
||||||
osg::ref_ptr<osg::RenderBuffer> mFirstPersonDepthRBProxy;
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> mSceneTex;
|
TechniqueList mTechniques;
|
||||||
osg::ref_ptr<osg::Texture2D> mDepthTex;
|
TechniqueList mTemplates;
|
||||||
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
|
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> mTechniqueFileMap;
|
||||||
|
|
||||||
|
int mSamples;
|
||||||
|
|
||||||
|
bool mDirty;
|
||||||
|
size_t mDirtyFrameId;
|
||||||
|
|
||||||
|
RenderingManager& mRendering;
|
||||||
|
osgViewer::Viewer* mViewer;
|
||||||
|
const VFS::Manager* mVFS;
|
||||||
|
|
||||||
|
bool mReload;
|
||||||
|
bool mEnabled;
|
||||||
|
bool mUsePostProcessing;
|
||||||
|
bool mSoftParticles;
|
||||||
|
bool mDisableDepthPasses;
|
||||||
|
|
||||||
|
size_t mLastFrameNumber;
|
||||||
|
float mLastSimulationTime;
|
||||||
|
|
||||||
|
bool mExteriorFlag;
|
||||||
|
bool mUnderwater;
|
||||||
|
bool mHDR;
|
||||||
|
bool mNormals;
|
||||||
|
bool mPrevNormals;
|
||||||
|
bool mNormalsSupported;
|
||||||
|
bool mUBO;
|
||||||
|
int mGLSLVersion;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> mMainTemplate;
|
||||||
|
|
||||||
|
osg::ref_ptr<fx::StateUpdater> mStateUpdater;
|
||||||
|
osg::ref_ptr<PingPongCull> mPingPongCull;
|
||||||
|
osg::ref_ptr<PingPongCanvas> mPingPongCanvas;
|
||||||
|
osg::ref_ptr<TransparentDepthBinCallback> mTransparentDepthPostPass;
|
||||||
|
|
||||||
|
int mWidth;
|
||||||
|
int mHeight;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -54,7 +54,9 @@
|
|||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/groundcoverstore.hpp"
|
#include "../mwworld/groundcoverstore.hpp"
|
||||||
#include "../mwgui/loadingscreen.hpp"
|
#include "../mwgui/loadingscreen.hpp"
|
||||||
|
#include "../mwgui/postprocessorhud.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
#include "sky.hpp"
|
#include "sky.hpp"
|
||||||
#include "effectmanager.hpp"
|
#include "effectmanager.hpp"
|
||||||
@ -114,7 +116,7 @@ namespace MWRender
|
|||||||
mProjectionMatrix = projectionMatrix;
|
mProjectionMatrix = projectionMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
const osg::Matrixf& projectionMatrix() const
|
const osg::Matrixf& getProjectionMatrix() const
|
||||||
{
|
{
|
||||||
return mProjectionMatrix;
|
return mProjectionMatrix;
|
||||||
}
|
}
|
||||||
@ -208,7 +210,6 @@ namespace MWRender
|
|||||||
mPlayerPos = playerPos;
|
mPlayerPos = playerPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mLinearFac;
|
float mLinearFac;
|
||||||
float mNear;
|
float mNear;
|
||||||
@ -411,9 +412,11 @@ namespace MWRender
|
|||||||
globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0";
|
globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0";
|
||||||
globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
|
globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
|
||||||
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
||||||
|
globalDefines["refraction_enabled"] = "0";
|
||||||
globalDefines["useGPUShader4"] = "0";
|
globalDefines["useGPUShader4"] = "0";
|
||||||
globalDefines["useOVR_multiview"] = "0";
|
globalDefines["useOVR_multiview"] = "0";
|
||||||
globalDefines["numViews"] = "1";
|
globalDefines["numViews"] = "1";
|
||||||
|
globalDefines["disableNormals"] = "1";
|
||||||
|
|
||||||
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
||||||
globalDefines[itr->first] = itr->second;
|
globalDefines[itr->first] = itr->second;
|
||||||
@ -502,12 +505,9 @@ namespace MWRender
|
|||||||
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater();
|
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater();
|
||||||
rootNode->addCullCallback(mPerViewUniformStateUpdater);
|
rootNode->addCullCallback(mPerViewUniformStateUpdater);
|
||||||
|
|
||||||
mPostProcessor = new PostProcessor(viewer, mRootNode);
|
mPostProcessor = new PostProcessor(*this, viewer, mRootNode, resourceSystem->getVFS());
|
||||||
resourceSystem->getSceneManager()->setDepthFormat(SceneUtil::AutoDepth::depthInternalFormat());
|
resourceSystem->getSceneManager()->setOpaqueDepthTex(mPostProcessor->getTexture(PostProcessor::Tex_OpaqueDepth, 0), mPostProcessor->getTexture(PostProcessor::Tex_OpaqueDepth, 1));
|
||||||
resourceSystem->getSceneManager()->setOpaqueDepthTex(mPostProcessor->getOpaqueDepthTex());
|
resourceSystem->getSceneManager()->setSupportsNormalsRT(mPostProcessor->getSupportsNormalsRT());
|
||||||
|
|
||||||
if (reverseZ && !SceneUtil::isFloatingPointDepthFormat(SceneUtil::AutoDepth::depthInternalFormat()))
|
|
||||||
Log(Debug::Warning) << "Floating point depth format not in use but reverse-z buffer is enabled, consider disabling it.";
|
|
||||||
|
|
||||||
// water goes after terrain for correct waterculling order
|
// water goes after terrain for correct waterculling order
|
||||||
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
||||||
@ -692,9 +692,10 @@ namespace MWRender
|
|||||||
|
|
||||||
void RenderingManager::configureAmbient(const ESM::Cell *cell)
|
void RenderingManager::configureAmbient(const ESM::Cell *cell)
|
||||||
{
|
{
|
||||||
|
bool isInterior = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
|
||||||
bool needsAdjusting = false;
|
bool needsAdjusting = false;
|
||||||
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
|
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
|
||||||
needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
|
needsAdjusting = isInterior;
|
||||||
|
|
||||||
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
|
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
|
||||||
|
|
||||||
@ -724,11 +725,14 @@ namespace MWRender
|
|||||||
mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));
|
mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular)
|
void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular, float sunVis)
|
||||||
{
|
{
|
||||||
// need to wrap this in a StateUpdater?
|
// need to wrap this in a StateUpdater?
|
||||||
mSunLight->setDiffuse(diffuse);
|
mSunLight->setDiffuse(diffuse);
|
||||||
mSunLight->setSpecular(specular);
|
mSunLight->setSpecular(specular);
|
||||||
|
|
||||||
|
mPostProcessor->getStateUpdater()->setSunColor(diffuse);
|
||||||
|
mPostProcessor->getStateUpdater()->setSunVis(sunVis);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::setSunDirection(const osg::Vec3f &direction)
|
void RenderingManager::setSunDirection(const osg::Vec3f &direction)
|
||||||
@ -738,6 +742,8 @@ namespace MWRender
|
|||||||
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
|
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
|
||||||
|
|
||||||
mSky->setSunDirection(position);
|
mSky->setSunDirection(position);
|
||||||
|
|
||||||
|
mPostProcessor->getStateUpdater()->setSunPos(mSunLight->getPosition(), mNight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::addCell(const MWWorld::CellStore *store)
|
void RenderingManager::addCell(const MWWorld::CellStore *store)
|
||||||
@ -779,6 +785,7 @@ namespace MWRender
|
|||||||
mShadowManager->enableOutdoorMode();
|
mShadowManager->enableOutdoorMode();
|
||||||
else
|
else
|
||||||
mShadowManager->enableIndoorMode();
|
mShadowManager->enableIndoorMode();
|
||||||
|
mPostProcessor->getStateUpdater()->setIsInterior(!enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenderingManager::toggleBorders()
|
bool RenderingManager::toggleBorders()
|
||||||
@ -877,9 +884,28 @@ namespace MWRender
|
|||||||
mCamera->update(dt, paused);
|
mCamera->update(dt, paused);
|
||||||
|
|
||||||
bool isUnderwater = mWater->isUnderwater(mCamera->getPosition());
|
bool isUnderwater = mWater->isUnderwater(mCamera->getPosition());
|
||||||
mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
|
|
||||||
mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
|
float fogStart = mFog->getFogStart(isUnderwater);
|
||||||
setFogColor(mFog->getFogColor(isUnderwater));
|
float fogEnd = mFog->getFogEnd(isUnderwater);
|
||||||
|
osg::Vec4f fogColor = mFog->getFogColor(isUnderwater);
|
||||||
|
|
||||||
|
mStateUpdater->setFogStart(fogStart);
|
||||||
|
mStateUpdater->setFogEnd(fogEnd);
|
||||||
|
setFogColor(fogColor);
|
||||||
|
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
const auto& stateUpdater = mPostProcessor->getStateUpdater();
|
||||||
|
|
||||||
|
stateUpdater->setFogRange(fogStart, fogEnd);
|
||||||
|
stateUpdater->setNearFar(mNearClip, mViewDistance);
|
||||||
|
stateUpdater->setIsUnderwater(isUnderwater);
|
||||||
|
stateUpdater->setFogColor(fogColor);
|
||||||
|
stateUpdater->setGameHour(world->getTimeStamp().getHour());
|
||||||
|
stateUpdater->setWeatherId(world->getCurrentWeather());
|
||||||
|
stateUpdater->setNextWeatherId(world->getNextWeather());
|
||||||
|
stateUpdater->setWeatherTransition(world->getWeatherTransition());
|
||||||
|
stateUpdater->setWindSpeed(world->getWindSpeed());
|
||||||
|
mPostProcessor->setUnderwaterFlag(isUnderwater);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
|
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
|
||||||
@ -939,6 +965,8 @@ namespace MWRender
|
|||||||
mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water));
|
mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water));
|
||||||
mWater->setHeight(height);
|
mWater->setHeight(height);
|
||||||
mSky->setWaterHeight(height);
|
mSky->setWaterHeight(height);
|
||||||
|
|
||||||
|
mPostProcessor->getStateUpdater()->setWaterHeight(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::screenshot(osg::Image* image, int w, int h)
|
void RenderingManager::screenshot(osg::Image* image, int w, int h)
|
||||||
@ -1131,6 +1159,11 @@ namespace MWRender
|
|||||||
return mObjects->getAnimation(ptr);
|
return mObjects->getAnimation(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostProcessor* RenderingManager::getPostProcessor()
|
||||||
|
{
|
||||||
|
return mPostProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
void RenderingManager::setupPlayer(const MWWorld::Ptr &player)
|
void RenderingManager::setupPlayer(const MWWorld::Ptr &player)
|
||||||
{
|
{
|
||||||
if (!mPlayerNode)
|
if (!mPlayerNode)
|
||||||
@ -1218,9 +1251,9 @@ namespace MWRender
|
|||||||
{
|
{
|
||||||
auto res = Stereo::Manager::instance().eyeResolution();
|
auto res = Stereo::Manager::instance().eyeResolution();
|
||||||
mSharedUniformStateUpdater->setScreenRes(res.x(), res.y());
|
mSharedUniformStateUpdater->setScreenRes(res.x(), res.y());
|
||||||
Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->projectionMatrix());
|
Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->getProjectionMatrix());
|
||||||
}
|
}
|
||||||
else
|
else if (!mPostProcessor->isEnabled())
|
||||||
{
|
{
|
||||||
mSharedUniformStateUpdater->setScreenRes(width, height);
|
mSharedUniformStateUpdater->setScreenRes(width, height);
|
||||||
}
|
}
|
||||||
@ -1229,6 +1262,17 @@ namespace MWRender
|
|||||||
// Limit FOV here just for sure, otherwise viewing distance can be too high.
|
// Limit FOV here just for sure, otherwise viewing distance can be too high.
|
||||||
float distanceMult = std::cos(osg::DegreesToRadians(std::min(fov, 140.f))/2.f);
|
float distanceMult = std::cos(osg::DegreesToRadians(std::min(fov, 140.f))/2.f);
|
||||||
mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
|
mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
|
||||||
|
|
||||||
|
if (mPostProcessor)
|
||||||
|
{
|
||||||
|
mPostProcessor->getStateUpdater()->setProjectionMatrix(mPerViewUniformStateUpdater->getProjectionMatrix());
|
||||||
|
mPostProcessor->getStateUpdater()->setFov(fov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderingManager::setScreenRes(int width, int height)
|
||||||
|
{
|
||||||
|
mSharedUniformStateUpdater->setScreenRes(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::updateTextureFiltering()
|
void RenderingManager::updateTextureFiltering()
|
||||||
@ -1335,6 +1379,17 @@ namespace MWRender
|
|||||||
mViewer->startThreading();
|
mViewer->startThreading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (it->first == "Post Processing" && it->second == "enabled")
|
||||||
|
{
|
||||||
|
if (Settings::Manager::getBool("enabled", "Post Processing"))
|
||||||
|
mPostProcessor->enable();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mPostProcessor->disable();
|
||||||
|
if (auto* hud = MWBase::Environment::get().getWindowManager()->getPostProcessorHud())
|
||||||
|
hud->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateProjection)
|
if (updateProjection)
|
||||||
|
@ -128,7 +128,8 @@ namespace MWRender
|
|||||||
void skySetMoonColour(bool red);
|
void skySetMoonColour(bool red);
|
||||||
|
|
||||||
void setSunDirection(const osg::Vec3f& direction);
|
void setSunDirection(const osg::Vec3f& direction);
|
||||||
void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular);
|
void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular, float sunVis);
|
||||||
|
void setNight(bool isNight) { mNight = isNight; }
|
||||||
|
|
||||||
void configureAmbient(const ESM::Cell* cell);
|
void configureAmbient(const ESM::Cell* cell);
|
||||||
void configureFog(const ESM::Cell* cell);
|
void configureFog(const ESM::Cell* cell);
|
||||||
@ -192,6 +193,8 @@ namespace MWRender
|
|||||||
Animation* getAnimation(const MWWorld::Ptr& ptr);
|
Animation* getAnimation(const MWWorld::Ptr& ptr);
|
||||||
const Animation* getAnimation(const MWWorld::ConstPtr& ptr) const;
|
const Animation* getAnimation(const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
|
PostProcessor* getPostProcessor();
|
||||||
|
|
||||||
void addWaterRippleEmitter(const MWWorld::Ptr& ptr);
|
void addWaterRippleEmitter(const MWWorld::Ptr& ptr);
|
||||||
void removeWaterRippleEmitter(const MWWorld::Ptr& ptr);
|
void removeWaterRippleEmitter(const MWWorld::Ptr& ptr);
|
||||||
void emitWaterRipple(const osg::Vec3f& pos);
|
void emitWaterRipple(const osg::Vec3f& pos);
|
||||||
@ -247,6 +250,8 @@ namespace MWRender
|
|||||||
|
|
||||||
void updateProjectionMatrix();
|
void updateProjectionMatrix();
|
||||||
|
|
||||||
|
void setScreenRes(int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateTextureFiltering();
|
void updateTextureFiltering();
|
||||||
void updateAmbient();
|
void updateAmbient();
|
||||||
@ -310,6 +315,7 @@ namespace MWRender
|
|||||||
float mFieldOfView;
|
float mFieldOfView;
|
||||||
float mFirstPersonFieldOfView;
|
float mFirstPersonFieldOfView;
|
||||||
bool mUpdateProjectionMatrix = false;
|
bool mUpdateProjectionMatrix = false;
|
||||||
|
bool mNight = false;
|
||||||
|
|
||||||
void operator = (const RenderingManager&);
|
void operator = (const RenderingManager&);
|
||||||
RenderingManager(const RenderingManager&);
|
RenderingManager(const RenderingManager&);
|
||||||
|
@ -106,17 +106,16 @@ namespace MWRender
|
|||||||
|
|
||||||
if (ext)
|
if (ext)
|
||||||
{
|
{
|
||||||
|
size_t frameId = renderInfo.getState()->getFrameStamp()->getFrameNumber() % 2;
|
||||||
osg::FrameBufferObject* fbo = nullptr;
|
osg::FrameBufferObject* fbo = nullptr;
|
||||||
|
|
||||||
if (Stereo::getStereo())
|
if (Stereo::getStereo())
|
||||||
fbo = Stereo::Manager::instance().multiviewFramebuffer()->layerFbo(0);
|
fbo = Stereo::Manager::instance().multiviewFramebuffer()->layerFbo(0);
|
||||||
else if (postProcessor)
|
else if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_Primary, frameId))
|
||||||
fbo = postProcessor->getFbo();
|
fbo = postProcessor->getFbo(PostProcessor::FBO_Primary, frameId);
|
||||||
|
|
||||||
if (fbo)
|
if (fbo)
|
||||||
{
|
fbo->apply(*renderInfo.getState(), osg::FrameBufferObject::READ_FRAMEBUFFER);
|
||||||
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo->getHandle(renderInfo.getContextID()));
|
|
||||||
renderInfo.getState()->glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
|
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
|
||||||
|
@ -248,6 +248,7 @@ namespace MWRender
|
|||||||
, mBaseWindSpeed(0.f)
|
, mBaseWindSpeed(0.f)
|
||||||
, mEnabled(true)
|
, mEnabled(true)
|
||||||
, mSunEnabled(true)
|
, mSunEnabled(true)
|
||||||
|
, mSunglareEnabled(true)
|
||||||
, mPrecipitationAlpha(0.f)
|
, mPrecipitationAlpha(0.f)
|
||||||
, mDirtyParticlesEffect(false)
|
, mDirtyParticlesEffect(false)
|
||||||
{
|
{
|
||||||
@ -303,6 +304,7 @@ namespace MWRender
|
|||||||
atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater);
|
atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater);
|
||||||
|
|
||||||
mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager));
|
mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager));
|
||||||
|
mSun->setSunglare(mSunglareEnabled);
|
||||||
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
|
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
|
||||||
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
|
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
|
||||||
|
|
||||||
@ -776,6 +778,14 @@ namespace MWRender
|
|||||||
return mBaseWindSpeed;
|
return mBaseWindSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkyManager::setSunglare(bool enabled)
|
||||||
|
{
|
||||||
|
mSunglareEnabled = enabled;
|
||||||
|
|
||||||
|
if (mSun)
|
||||||
|
mSun->setSunglare(mSunglareEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
void SkyManager::sunEnable()
|
void SkyManager::sunEnable()
|
||||||
{
|
{
|
||||||
if (!mCreated) return;
|
if (!mCreated) return;
|
||||||
|
@ -96,6 +96,8 @@ namespace MWRender
|
|||||||
|
|
||||||
float getBaseWindSpeed() const;
|
float getBaseWindSpeed() const;
|
||||||
|
|
||||||
|
void setSunglare(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void create();
|
void create();
|
||||||
///< no need to call this, automatically done on first enable()
|
///< no need to call this, automatically done on first enable()
|
||||||
@ -184,6 +186,7 @@ namespace MWRender
|
|||||||
|
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
bool mSunEnabled;
|
bool mSunEnabled;
|
||||||
|
bool mSunglareEnabled;
|
||||||
|
|
||||||
float mPrecipitationAlpha;
|
float mPrecipitationAlpha;
|
||||||
bool mDirtyParticlesEffect;
|
bool mDirtyParticlesEffect;
|
||||||
|
@ -812,6 +812,12 @@ namespace MWRender
|
|||||||
mSunGlareCallback->setTimeOfDayFade(val);
|
mSunGlareCallback->setTimeOfDayFade(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sun::setSunglare(bool enabled)
|
||||||
|
{
|
||||||
|
mSunGlareNode->setNodeMask(enabled ? ~0u : 0);
|
||||||
|
mSunFlashNode->setNodeMask(enabled ? ~0u : 0);
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::OcclusionQueryNode> Sun::createOcclusionQueryNode(osg::Group* parent, bool queryVisible)
|
osg::ref_ptr<osg::OcclusionQueryNode> Sun::createOcclusionQueryNode(osg::Group* parent, bool queryVisible)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
|
osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
|
||||||
|
@ -248,6 +248,7 @@ namespace MWRender
|
|||||||
|
|
||||||
void setDirection(const osg::Vec3f& direction);
|
void setDirection(const osg::Vec3f& direction);
|
||||||
void setGlareTimeOfDayFade(float val);
|
void setGlareTimeOfDayFade(float val);
|
||||||
|
void setSunglare(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
|
/// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
|
||||||
|
89
apps/openmw/mwrender/transparentpass.cpp
Normal file
89
apps/openmw/mwrender/transparentpass.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "transparentpass.hpp"
|
||||||
|
|
||||||
|
#include <osg/BlendFunc>
|
||||||
|
#include <osg/Texture2D>
|
||||||
|
|
||||||
|
#include <osgUtil/RenderStage>
|
||||||
|
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
TransparentDepthBinCallback::TransparentDepthBinCallback(Shader::ShaderManager& shaderManager, bool postPass)
|
||||||
|
: mStateSet(new osg::StateSet)
|
||||||
|
, mPostPass(postPass)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Image> image = new osg::Image;
|
||||||
|
image->allocateImage(1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||||
|
image->setColor(osg::Vec4(1,1,1,1), 0, 0);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> dummyTexture = new osg::Texture2D(image);
|
||||||
|
|
||||||
|
constexpr osg::StateAttribute::OverrideValue modeOff = osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE;
|
||||||
|
constexpr osg::StateAttribute::OverrideValue modeOn = osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE;
|
||||||
|
|
||||||
|
mStateSet->setTextureAttributeAndModes(0, dummyTexture);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Shader> vertex = shaderManager.getShader("blended_depth_postpass_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||||
|
osg::ref_ptr<osg::Shader> fragment = shaderManager.getShader("blended_depth_postpass_fragment.glsl", {}, osg::Shader::FRAGMENT);
|
||||||
|
|
||||||
|
mStateSet->setAttributeAndModes(new osg::BlendFunc, modeOff);
|
||||||
|
mStateSet->setAttributeAndModes(shaderManager.getProgram(vertex, fragment), modeOn);
|
||||||
|
|
||||||
|
for (unsigned int unit = 1; unit < 8; ++unit)
|
||||||
|
mStateSet->setTextureMode(unit, GL_TEXTURE_2D, modeOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransparentDepthBinCallback::drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
|
||||||
|
{
|
||||||
|
osg::State& state = *renderInfo.getState();
|
||||||
|
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
|
||||||
|
|
||||||
|
bool validFbo = false;
|
||||||
|
unsigned int frameId = state.getFrameStamp()->getFrameNumber() % 2;
|
||||||
|
|
||||||
|
const auto& fbo = mFbo[frameId];
|
||||||
|
const auto& msaaFbo = mMsaaFbo[frameId];
|
||||||
|
const auto& opaqueFbo = mOpaqueFbo[frameId];
|
||||||
|
|
||||||
|
if (bin->getStage()->getMultisampleResolveFramebufferObject() && bin->getStage()->getMultisampleResolveFramebufferObject() == fbo)
|
||||||
|
validFbo = true;
|
||||||
|
else if (bin->getStage()->getFrameBufferObject() && (bin->getStage()->getFrameBufferObject() == fbo || bin->getStage()->getFrameBufferObject() == msaaFbo))
|
||||||
|
validFbo = true;
|
||||||
|
|
||||||
|
if (!validFbo)
|
||||||
|
{
|
||||||
|
bin->drawImplementation(renderInfo, previous);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const osg::Texture* tex = opaqueFbo->getAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER).getTexture();
|
||||||
|
|
||||||
|
opaqueFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
ext->glBlitFramebuffer(0, 0, tex->getTextureWidth(), tex->getTextureHeight(), 0, 0, tex->getTextureWidth(), tex->getTextureHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
|
if (msaaFbo)
|
||||||
|
msaaFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
else
|
||||||
|
fbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
// draws scene into primary attachments
|
||||||
|
bin->drawImplementation(renderInfo, previous);
|
||||||
|
|
||||||
|
if (!mPostPass)
|
||||||
|
return;
|
||||||
|
|
||||||
|
opaqueFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
|
||||||
|
bin->setStateSet(mStateSet);
|
||||||
|
// draws transparent post-pass to populate a postprocess friendly depth texture with alpha-clipped geometry
|
||||||
|
bin->drawImplementation(renderInfo, previous);
|
||||||
|
bin->setStateSet(restore);
|
||||||
|
|
||||||
|
if (!msaaFbo)
|
||||||
|
fbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
apps/openmw/mwrender/transparentpass.hpp
Normal file
38
apps/openmw/mwrender/transparentpass.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef OPENMW_MWRENDER_TRANSPARENTPASS_H
|
||||||
|
#define OPENMW_MWRENDER_TRANSPARENTPASS_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <osg/FrameBufferObject>
|
||||||
|
#include <osg/StateSet>
|
||||||
|
|
||||||
|
#include <osgUtil/RenderBin>
|
||||||
|
|
||||||
|
#include "postprocessor.hpp"
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
class ShaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
class TransparentDepthBinCallback : public osgUtil::RenderBin::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TransparentDepthBinCallback(Shader::ShaderManager& shaderManager, bool postPass);
|
||||||
|
|
||||||
|
void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override;
|
||||||
|
|
||||||
|
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mFbo;
|
||||||
|
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mMsaaFbo;
|
||||||
|
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mOpaqueFbo;
|
||||||
|
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osg::StateSet> mStateSet;
|
||||||
|
bool mPostPass;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -267,6 +267,7 @@ public:
|
|||||||
: RTTNode(rttSize, rttSize, 0, false, 1, StereoAwareness::Aware)
|
: RTTNode(rttSize, rttSize, 0, false, 1, StereoAwareness::Aware)
|
||||||
, mNodeMask(Refraction::sDefaultCullMask)
|
, mNodeMask(Refraction::sDefaultCullMask)
|
||||||
{
|
{
|
||||||
|
setDepthBufferInternalFormat(GL_DEPTH24_STENCIL8);
|
||||||
mClipCullNode = new ClipCullNode;
|
mClipCullNode = new ClipCullNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +343,7 @@ public:
|
|||||||
: RTTNode(rttSize, rttSize, 0, false, 0, StereoAwareness::Aware)
|
: RTTNode(rttSize, rttSize, 0, false, 0, StereoAwareness::Aware)
|
||||||
{
|
{
|
||||||
setInterior(isInterior);
|
setInterior(isInterior);
|
||||||
|
setDepthBufferInternalFormat(GL_DEPTH24_STENCIL8);
|
||||||
mClipCullNode = new ClipCullNode;
|
mClipCullNode = new ClipCullNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "../mwrender/renderingmanager.hpp"
|
#include "../mwrender/renderingmanager.hpp"
|
||||||
#include "../mwrender/landmanager.hpp"
|
#include "../mwrender/landmanager.hpp"
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
#include "../mwphysics/physicssystem.hpp"
|
#include "../mwphysics/physicssystem.hpp"
|
||||||
#include "../mwphysics/actor.hpp"
|
#include "../mwphysics/actor.hpp"
|
||||||
@ -860,6 +861,8 @@ namespace MWWorld
|
|||||||
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
||||||
|
|
||||||
mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);
|
mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->getPostProcessor()->setExteriorFlag(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
||||||
@ -879,6 +882,8 @@ namespace MWWorld
|
|||||||
|
|
||||||
if (changeEvent)
|
if (changeEvent)
|
||||||
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
|
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->getPostProcessor()->setExteriorFlag(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
CellStore* Scene::getCurrentCell ()
|
CellStore* Scene::getCurrentCell ()
|
||||||
|
@ -786,6 +786,7 @@ namespace MWWorld
|
|||||||
-0.268f, // approx tan( -15 degrees )
|
-0.268f, // approx tan( -15 degrees )
|
||||||
static_cast<float>(sin(theta)));
|
static_cast<float>(sin(theta)));
|
||||||
mRendering.setSunDirection( final * -1 );
|
mRendering.setSunDirection( final * -1 );
|
||||||
|
mRendering.setNight(is_night);
|
||||||
}
|
}
|
||||||
|
|
||||||
float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog");
|
float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog");
|
||||||
@ -807,7 +808,7 @@ namespace MWWorld
|
|||||||
mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor,
|
mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor,
|
||||||
mResult.mDLFogOffset/100.0f, mResult.mFogColor);
|
mResult.mDLFogOffset/100.0f, mResult.mFogColor);
|
||||||
mRendering.setAmbientColour(mResult.mAmbientColor);
|
mRendering.setAmbientColour(mResult.mAmbientColor);
|
||||||
mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade);
|
mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade, mResult.mGlareView * glareFade);
|
||||||
|
|
||||||
mRendering.getSkyManager()->setWeather(mResult);
|
mRendering.getSkyManager()->setWeather(mResult);
|
||||||
|
|
||||||
@ -857,11 +858,6 @@ namespace MWWorld
|
|||||||
mFastForward = !incremental ? true : mFastForward;
|
mFastForward = !incremental ? true : mFastForward;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int WeatherManager::getWeatherID() const
|
|
||||||
{
|
|
||||||
return mCurrentWeather;
|
|
||||||
}
|
|
||||||
|
|
||||||
NightDayMode WeatherManager::getNightDayMode() const
|
NightDayMode WeatherManager::getNightDayMode() const
|
||||||
{
|
{
|
||||||
return mNightDayMode;
|
return mNightDayMode;
|
||||||
|
@ -307,7 +307,11 @@ namespace MWWorld
|
|||||||
|
|
||||||
void advanceTime(double hours, bool incremental);
|
void advanceTime(double hours, bool incremental);
|
||||||
|
|
||||||
unsigned int getWeatherID() const;
|
int getWeatherID() const { return mCurrentWeather; }
|
||||||
|
|
||||||
|
int getNextWeatherID() const { return mNextWeather; }
|
||||||
|
|
||||||
|
float getTransitionFactor() const { return mTransitionFactor; }
|
||||||
|
|
||||||
bool useTorches(float hour) const;
|
bool useTorches(float hour) const;
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
#include "../mwrender/renderingmanager.hpp"
|
#include "../mwrender/renderingmanager.hpp"
|
||||||
#include "../mwrender/camera.hpp"
|
#include "../mwrender/camera.hpp"
|
||||||
#include "../mwrender/vismask.hpp"
|
#include "../mwrender/vismask.hpp"
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
#include "../mwscript/globalscripts.hpp"
|
#include "../mwscript/globalscripts.hpp"
|
||||||
|
|
||||||
@ -2045,6 +2046,16 @@ namespace MWWorld
|
|||||||
return mWeatherManager->getWeatherID();
|
return mWeatherManager->getWeatherID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int World::getNextWeather() const
|
||||||
|
{
|
||||||
|
return mWeatherManager->getNextWeatherID();
|
||||||
|
}
|
||||||
|
|
||||||
|
float World::getWeatherTransition() const
|
||||||
|
{
|
||||||
|
return mWeatherManager->getTransitionFactor();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int World::getNightDayMode() const
|
unsigned int World::getNightDayMode() const
|
||||||
{
|
{
|
||||||
return mWeatherManager->getNightDayMode();
|
return mWeatherManager->getNightDayMode();
|
||||||
@ -3986,4 +3997,8 @@ namespace MWWorld
|
|||||||
return mPrng;
|
return mPrng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWRender::PostProcessor* World::getPostProcessor()
|
||||||
|
{
|
||||||
|
return mRendering->getPostProcessor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ namespace MWRender
|
|||||||
class SkyManager;
|
class SkyManager;
|
||||||
class Animation;
|
class Animation;
|
||||||
class Camera;
|
class Camera;
|
||||||
|
class PostProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ToUTF8
|
namespace ToUTF8
|
||||||
@ -329,6 +330,10 @@ namespace MWWorld
|
|||||||
|
|
||||||
int getCurrentWeather() const override;
|
int getCurrentWeather() const override;
|
||||||
|
|
||||||
|
int getNextWeather() const override;
|
||||||
|
|
||||||
|
float getWeatherTransition() const override;
|
||||||
|
|
||||||
unsigned int getNightDayMode() const override;
|
unsigned int getNightDayMode() const override;
|
||||||
|
|
||||||
int getMasserPhase() const override;
|
int getMasserPhase() const override;
|
||||||
@ -747,6 +752,8 @@ namespace MWWorld
|
|||||||
Misc::Rng::Generator& getPrng() override;
|
Misc::Rng::Generator& getPrng() override;
|
||||||
|
|
||||||
MWRender::RenderingManager* getRenderingManager() override { return mRendering.get(); }
|
MWRender::RenderingManager* getRenderingManager() override { return mRendering.get(); }
|
||||||
|
|
||||||
|
MWRender::PostProcessor* getPostProcessor() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
|||||||
serialization/integration.cpp
|
serialization/integration.cpp
|
||||||
|
|
||||||
settings/parser.cpp
|
settings/parser.cpp
|
||||||
|
settings/shadermanager.cpp
|
||||||
|
|
||||||
shader/parsedefines.cpp
|
shader/parsedefines.cpp
|
||||||
shader/parsefors.cpp
|
shader/parsefors.cpp
|
||||||
@ -75,6 +76,9 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
|||||||
toutf8/toutf8.cpp
|
toutf8/toutf8.cpp
|
||||||
|
|
||||||
esm4/includes.cpp
|
esm4/includes.cpp
|
||||||
|
|
||||||
|
fx/lexer.cpp
|
||||||
|
fx/technique.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||||
|
216
apps/openmw_test_suite/fx/lexer.cpp
Normal file
216
apps/openmw_test_suite/fx/lexer.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#include <components/fx/lexer.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace testing;
|
||||||
|
using namespace fx::Lexer;
|
||||||
|
|
||||||
|
struct LexerTest : Test {};
|
||||||
|
|
||||||
|
struct LexerSingleTokenTest : Test
|
||||||
|
{
|
||||||
|
template <class Token>
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
const std::string content = std::string(Token::repr);
|
||||||
|
Lexer lexer(content);
|
||||||
|
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Token>(lexer.next()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_shared) { test<Shared>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_technique) { test<Technique>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_main_pass) { test<Main_Pass>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_render_target) { test<Render_Target>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_vertex) { test<Vertex>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_fragment) { test<Fragment>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_compute) { test<Compute>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_sampler_1d) { test<Sampler_1D>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_sampler_2d) { test<Sampler_2D>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_sampler_3d) { test<Sampler_3D>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_true) { test<True>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_false) { test<False>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_vec2) { test<Vec2>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_vec3) { test<Vec3>(); }
|
||||||
|
TEST_F(LexerSingleTokenTest, single_token_vec4) { test<Vec4>(); }
|
||||||
|
|
||||||
|
TEST(LexerTest, peek_whitespace_only_content_should_be_eof)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
|
||||||
|
)");
|
||||||
|
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Eof>(lexer.peek()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, float_with_no_prefixed_digits)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
0.123;
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Float>(token));
|
||||||
|
EXPECT_FLOAT_EQ(std::get<Float>(token).value, 0.123f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, float_with_alpha_prefix)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
abc.123;
|
||||||
|
)");
|
||||||
|
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Literal>(lexer.next()));
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Float>(token));
|
||||||
|
EXPECT_FLOAT_EQ(std::get<Float>(token).value, 0.123f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, float_with_numeric_prefix)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
123.123;
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Float>(token));
|
||||||
|
EXPECT_FLOAT_EQ(std::get<Float>(token).value, 123.123f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, int_should_not_be_float)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
123
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Integer>(token));
|
||||||
|
EXPECT_EQ(std::get<Integer>(token).value, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, simple_string)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
"test string"
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<String>(token));
|
||||||
|
|
||||||
|
std::string parsed = std::string(std::get<String>(token).value);
|
||||||
|
EXPECT_EQ("test string", parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, fail_on_unterminated_double_quotes)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
"unterminated string'
|
||||||
|
)");
|
||||||
|
|
||||||
|
EXPECT_THROW(lexer.next(), LexerException);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, multiline_strings_with_single_quotes)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
"string that is
|
||||||
|
on multiple with 'single quotes'
|
||||||
|
and correctly terminated!"
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto token = lexer.next();
|
||||||
|
EXPECT_TRUE(std::holds_alternative<String>(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, fail_on_unterminated_double_quotes_with_multiline_strings)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(
|
||||||
|
"string that is
|
||||||
|
on multiple with 'single quotes'
|
||||||
|
and but is unterminated :(
|
||||||
|
)");
|
||||||
|
|
||||||
|
EXPECT_THROW(lexer.next(), LexerException);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, jump_with_single_nested_bracket)
|
||||||
|
{
|
||||||
|
const std::string content = R"(
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}})";
|
||||||
|
|
||||||
|
const std::string expected = content.substr(0, content.size() - 1);
|
||||||
|
|
||||||
|
Lexer lexer(content);
|
||||||
|
|
||||||
|
auto block = lexer.jump();
|
||||||
|
|
||||||
|
EXPECT_NE(block, std::nullopt);
|
||||||
|
EXPECT_EQ(expected, std::string(block.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, jump_with_single_line_comments_and_mismatching_brackets)
|
||||||
|
{
|
||||||
|
const std::string content = R"(
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// }
|
||||||
|
return 0;
|
||||||
|
}})";
|
||||||
|
|
||||||
|
const std::string expected = content.substr(0, content.size() - 1);
|
||||||
|
|
||||||
|
Lexer lexer(content);
|
||||||
|
|
||||||
|
auto block = lexer.jump();
|
||||||
|
|
||||||
|
EXPECT_NE(block, std::nullopt);
|
||||||
|
EXPECT_EQ(expected, std::string(block.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, jump_with_multi_line_comments_and_mismatching_brackets)
|
||||||
|
{
|
||||||
|
const std::string content = R"(
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}})";
|
||||||
|
|
||||||
|
const std::string expected = content.substr(0, content.size() - 1);
|
||||||
|
|
||||||
|
Lexer lexer(content);
|
||||||
|
|
||||||
|
auto block = lexer.jump();
|
||||||
|
|
||||||
|
EXPECT_NE(block, std::nullopt);
|
||||||
|
EXPECT_EQ(expected, std::string(block.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LexerTest, immediate_closed_blocks)
|
||||||
|
{
|
||||||
|
Lexer lexer(R"(block{})");
|
||||||
|
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Literal>(lexer.next()));
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Open_bracket>(lexer.next()));
|
||||||
|
auto block = lexer.jump();
|
||||||
|
EXPECT_TRUE(block.has_value());
|
||||||
|
EXPECT_TRUE(block.value().empty());
|
||||||
|
EXPECT_TRUE(std::holds_alternative<Close_bracket>(lexer.next()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
204
apps/openmw_test_suite/fx/technique.cpp
Normal file
204
apps/openmw_test_suite/fx/technique.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/fx/technique.hpp>
|
||||||
|
#include <components/resource/imagemanager.hpp>
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include "../lua/testing_util.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
TestFile technique_properties(R"(
|
||||||
|
fragment main {}
|
||||||
|
vertex main {}
|
||||||
|
technique {
|
||||||
|
passes = main;
|
||||||
|
version = "0.1a";
|
||||||
|
description = "description";
|
||||||
|
author = "author";
|
||||||
|
glsl_version = 330;
|
||||||
|
glsl_profile = "compatability";
|
||||||
|
glsl_extensions = GL_EXT_gpu_shader4, GL_ARB_uniform_buffer_object;
|
||||||
|
flags = disable_sunglare;
|
||||||
|
hdr = true;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
TestFile rendertarget_properties{R"(
|
||||||
|
render_target rendertarget {
|
||||||
|
width_ratio = 0.5;
|
||||||
|
height_ratio = 0.5;
|
||||||
|
internal_format = r16f;
|
||||||
|
source_type = float;
|
||||||
|
source_format = red;
|
||||||
|
mipmaps = true;
|
||||||
|
wrap_s = clamp_to_edge;
|
||||||
|
wrap_t = repeat;
|
||||||
|
min_filter = linear;
|
||||||
|
mag_filter = nearest;
|
||||||
|
}
|
||||||
|
fragment downsample2x(target=rendertarget) {
|
||||||
|
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor.r = omw_GetLastShader(omw_TexCoord).r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragment main { }
|
||||||
|
technique { passes = downsample2x, main; }
|
||||||
|
)"};
|
||||||
|
|
||||||
|
|
||||||
|
TestFile uniform_properties{R"(
|
||||||
|
uniform_vec4 uVec4 {
|
||||||
|
default = vec4(0,0,0,0);
|
||||||
|
min = vec4(0,1,0,0);
|
||||||
|
max = vec4(0,0,1,0);
|
||||||
|
step = 0.5;
|
||||||
|
header = "header";
|
||||||
|
static = true;
|
||||||
|
description = "description";
|
||||||
|
}
|
||||||
|
fragment main { }
|
||||||
|
technique { passes = main; }
|
||||||
|
)"};
|
||||||
|
|
||||||
|
|
||||||
|
TestFile missing_sampler_source{R"(
|
||||||
|
sampler_1d mysampler1d { }
|
||||||
|
fragment main { }
|
||||||
|
technique { passes = main; }
|
||||||
|
)"};
|
||||||
|
|
||||||
|
TestFile repeated_shared_block{R"(
|
||||||
|
shared {
|
||||||
|
float myfloat = 1.0;
|
||||||
|
}
|
||||||
|
shared {}
|
||||||
|
fragment main { }
|
||||||
|
technique { passes = main; }
|
||||||
|
)"};
|
||||||
|
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
using namespace fx;
|
||||||
|
|
||||||
|
struct TechniqueTest : Test
|
||||||
|
{
|
||||||
|
std::unique_ptr<VFS::Manager> mVFS;
|
||||||
|
Resource::ImageManager mImageManager;
|
||||||
|
std::unique_ptr<Technique> mTechnique;
|
||||||
|
|
||||||
|
TechniqueTest()
|
||||||
|
: mVFS(createTestVFS({
|
||||||
|
{"shaders/technique_properties.omwfx", &technique_properties},
|
||||||
|
{"shaders/rendertarget_properties.omwfx", &rendertarget_properties},
|
||||||
|
{"shaders/uniform_properties.omwfx", &uniform_properties},
|
||||||
|
{"shaders/missing_sampler_source.omwfx", &missing_sampler_source},
|
||||||
|
{"shaders/repeated_shared_block.omwfx", &repeated_shared_block},
|
||||||
|
}))
|
||||||
|
, mImageManager(mVFS.get())
|
||||||
|
{
|
||||||
|
Settings::Manager::setBool("radial fog", "Shaders", true);
|
||||||
|
Settings::Manager::setBool("stereo enabled", "Stereo", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compile(const std::string& name)
|
||||||
|
{
|
||||||
|
mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
|
||||||
|
mTechnique->compile();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(TechniqueTest, technique_properties)
|
||||||
|
{
|
||||||
|
std::unordered_set<std::string> targetExtensions = {
|
||||||
|
"GL_EXT_gpu_shader4",
|
||||||
|
"GL_ARB_uniform_buffer_object"
|
||||||
|
};
|
||||||
|
|
||||||
|
compile("technique_properties");
|
||||||
|
|
||||||
|
EXPECT_EQ(mTechnique->getVersion(), "0.1a");
|
||||||
|
EXPECT_EQ(mTechnique->getDescription(), "description");
|
||||||
|
EXPECT_EQ(mTechnique->getAuthor(), "author");
|
||||||
|
EXPECT_EQ(mTechnique->getGLSLVersion(), 330);
|
||||||
|
EXPECT_EQ(mTechnique->getGLSLProfile(), "compatability");
|
||||||
|
EXPECT_EQ(mTechnique->getGLSLExtensions(), targetExtensions);
|
||||||
|
EXPECT_EQ(mTechnique->getFlags(), Technique::Flag_Disable_SunGlare);
|
||||||
|
EXPECT_EQ(mTechnique->getHDR(), true);
|
||||||
|
EXPECT_EQ(mTechnique->getPasses().size(), 1);
|
||||||
|
EXPECT_EQ(mTechnique->getPasses().front()->getName(), "main");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TechniqueTest, rendertarget_properties)
|
||||||
|
{
|
||||||
|
compile("rendertarget_properties");
|
||||||
|
|
||||||
|
EXPECT_EQ(mTechnique->getRenderTargetsMap().size(), 1);
|
||||||
|
|
||||||
|
const std::string_view name = mTechnique->getRenderTargetsMap().begin()->first;
|
||||||
|
auto& rt = mTechnique->getRenderTargetsMap().begin()->second;
|
||||||
|
auto& texture = rt.mTarget;
|
||||||
|
|
||||||
|
EXPECT_EQ(name, "rendertarget");
|
||||||
|
EXPECT_EQ(rt.mMipMap, true);
|
||||||
|
EXPECT_EQ(rt.mSize.mWidthRatio, 0.5f);
|
||||||
|
EXPECT_EQ(rt.mSize.mHeightRatio, 0.5f);
|
||||||
|
EXPECT_EQ(texture->getWrap(osg::Texture::WRAP_S), osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
EXPECT_EQ(texture->getWrap(osg::Texture::WRAP_T), osg::Texture::REPEAT);
|
||||||
|
EXPECT_EQ(texture->getFilter(osg::Texture::MIN_FILTER), osg::Texture::LINEAR);
|
||||||
|
EXPECT_EQ(texture->getFilter(osg::Texture::MAG_FILTER), osg::Texture::NEAREST);
|
||||||
|
EXPECT_EQ(texture->getSourceType(), static_cast<GLenum>(GL_FLOAT));
|
||||||
|
EXPECT_EQ(texture->getSourceFormat(), static_cast<GLenum>(GL_RED));
|
||||||
|
EXPECT_EQ(texture->getInternalFormat(), static_cast<GLint>(GL_R16F));
|
||||||
|
|
||||||
|
EXPECT_EQ(mTechnique->getPasses().size(), 2);
|
||||||
|
EXPECT_EQ(mTechnique->getPasses()[0]->getTarget(), "rendertarget");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TechniqueTest, uniform_properties)
|
||||||
|
{
|
||||||
|
compile("uniform_properties");
|
||||||
|
|
||||||
|
EXPECT_EQ(mTechnique->getUniformMap().size(), 1);
|
||||||
|
|
||||||
|
const auto& uniform = mTechnique->getUniformMap().front();
|
||||||
|
|
||||||
|
EXPECT_TRUE(uniform->mStatic);
|
||||||
|
EXPECT_FLOAT_EQ(uniform->mStep, 0.5f);
|
||||||
|
EXPECT_EQ(uniform->getDefault<osg::Vec4f>(), osg::Vec4f(0,0,0,0));
|
||||||
|
EXPECT_EQ(uniform->getMin<osg::Vec4f>(), osg::Vec4f(0,1,0,0));
|
||||||
|
EXPECT_EQ(uniform->getMax<osg::Vec4f>(), osg::Vec4f(0,0,1,0));
|
||||||
|
EXPECT_EQ(uniform->mHeader, "header");
|
||||||
|
EXPECT_EQ(uniform->mDescription, "description");
|
||||||
|
EXPECT_EQ(uniform->mName, "uVec4");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TechniqueTest, fail_with_missing_source_for_sampler)
|
||||||
|
{
|
||||||
|
internal::CaptureStdout();
|
||||||
|
|
||||||
|
compile("missing_sampler_source");
|
||||||
|
|
||||||
|
std::string output = internal::GetCapturedStdout();
|
||||||
|
Log(Debug::Error) << output;
|
||||||
|
EXPECT_THAT(output, HasSubstr("sampler_1d 'mysampler1d' requires a filename"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TechniqueTest, fail_with_repeated_shared_block)
|
||||||
|
{
|
||||||
|
internal::CaptureStdout();
|
||||||
|
|
||||||
|
compile("repeated_shared_block");
|
||||||
|
|
||||||
|
std::string output = internal::GetCapturedStdout();
|
||||||
|
Log(Debug::Error) << output;
|
||||||
|
EXPECT_THAT(output, HasSubstr("repeated 'shared' block"));
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,11 @@ namespace
|
|||||||
return std::make_unique<std::stringstream>(mContent, std::ios_base::in);
|
return std::make_unique<std::stringstream>(mContent, std::ios_base::in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getPath() override
|
||||||
|
{
|
||||||
|
return "TestFile";
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string mContent;
|
const std::string mContent;
|
||||||
};
|
};
|
||||||
|
66
apps/openmw_test_suite/settings/shadermanager.cpp
Normal file
66
apps/openmw_test_suite/settings/shadermanager.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <components/settings/shadermanager.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace testing;
|
||||||
|
using namespace Settings;
|
||||||
|
|
||||||
|
struct ShaderSettingsTest : Test
|
||||||
|
{
|
||||||
|
template <typename F>
|
||||||
|
void withSettingsFile( const std::string& content, F&& f)
|
||||||
|
{
|
||||||
|
const auto path = std::string(UnitTest::GetInstance()->current_test_info()->name()) + ".yaml";
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream stream;
|
||||||
|
stream.open(path);
|
||||||
|
stream << content;
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
f(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ShaderSettingsTest, fail_to_fetch_then_set_and_succeed)
|
||||||
|
{
|
||||||
|
const std::string content =
|
||||||
|
R"YAML(
|
||||||
|
config:
|
||||||
|
shader:
|
||||||
|
vec3_uniform: [1.0, 2.0]
|
||||||
|
)YAML";
|
||||||
|
|
||||||
|
withSettingsFile(content, [this] (const auto& path) {
|
||||||
|
EXPECT_TRUE(ShaderManager::get().load(path));
|
||||||
|
EXPECT_FALSE(ShaderManager::get().getValue<osg::Vec3f>("shader", "vec3_uniform").has_value());
|
||||||
|
EXPECT_TRUE(ShaderManager::get().setValue<osg::Vec3f>("shader", "vec3_uniform", osg::Vec3f(1, 2, 3)));
|
||||||
|
EXPECT_TRUE(ShaderManager::get().getValue<osg::Vec3f>("shader", "vec3_uniform").has_value());
|
||||||
|
EXPECT_EQ(ShaderManager::get().getValue<osg::Vec3f>("shader", "vec3_uniform").value(), osg::Vec3f(1, 2, 3));
|
||||||
|
EXPECT_TRUE(ShaderManager::get().save());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ShaderSettingsTest, fail_to_load_file_then_fail_to_set_and_get)
|
||||||
|
{
|
||||||
|
const std::string content =
|
||||||
|
R"YAML(
|
||||||
|
config:
|
||||||
|
shader:
|
||||||
|
uniform: 12.0
|
||||||
|
>Defeated by a sideways carrot
|
||||||
|
)YAML";
|
||||||
|
|
||||||
|
withSettingsFile(content, [this] (const auto& path) {
|
||||||
|
EXPECT_FALSE(ShaderManager::get().load(path));
|
||||||
|
EXPECT_FALSE(ShaderManager::get().setValue("shader", "uniform", 12.0));
|
||||||
|
EXPECT_FALSE(ShaderManager::get().getValue<float>("shader", "uniform").has_value());
|
||||||
|
EXPECT_FALSE(ShaderManager::get().save());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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(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
|
add_component_dir (esm3
|
||||||
esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
|
esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
|
||||||
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst
|
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst
|
||||||
|
301
components/fx/lexer.cpp
Normal file
301
components/fx/lexer.cpp
Normal 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
75
components/fx/lexer.hpp
Normal 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
|
56
components/fx/lexer_types.hpp
Normal file
56
components/fx/lexer_types.hpp
Normal 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
|
133
components/fx/parse_constants.hpp
Normal file
133
components/fx/parse_constants.hpp
Normal 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
253
components/fx/pass.cpp
Normal 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
79
components/fx/pass.hpp
Normal 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
|
60
components/fx/stateupdater.cpp
Normal file
60
components/fx/stateupdater.cpp
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
192
components/fx/stateupdater.hpp
Normal file
192
components/fx/stateupdater.hpp
Normal 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
1049
components/fx/technique.cpp
Normal file
File diff suppressed because it is too large
Load Diff
300
components/fx/technique.hpp
Normal file
300
components/fx/technique.hpp
Normal 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
259
components/fx/types.hpp
Normal 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
164
components/fx/widgets.cpp
Normal 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
266
components/fx/widgets.hpp
Normal 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
|
@ -318,7 +318,7 @@ namespace Resource
|
|||||||
, mApplyLightingToEnvMaps(false)
|
, mApplyLightingToEnvMaps(false)
|
||||||
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||||
, mConvertAlphaTestToAlphaToCoverage(false)
|
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||||
, mDepthFormat(0)
|
, mSupportsNormalsRT(false)
|
||||||
, mSharedStateManager(new SharedStateManager)
|
, mSharedStateManager(new SharedStateManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
, mNifFileManager(nifFileManager)
|
, mNifFileManager(nifFileManager)
|
||||||
@ -348,7 +348,7 @@ namespace Resource
|
|||||||
if (forceShadersForNode)
|
if (forceShadersForNode)
|
||||||
shaderVisitor->setForceShaders(true);
|
shaderVisitor->setForceShaders(true);
|
||||||
if (disableSoftParticles)
|
if (disableSoftParticles)
|
||||||
shaderVisitor->setOpaqueDepthTex(nullptr);
|
shaderVisitor->setOpaqueDepthTex(nullptr, nullptr);
|
||||||
node->accept(*shaderVisitor);
|
node->accept(*shaderVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,16 +368,6 @@ namespace Resource
|
|||||||
return mClampLighting;
|
return mClampLighting;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::setDepthFormat(GLenum format)
|
|
||||||
{
|
|
||||||
mDepthFormat = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLenum SceneManager::getDepthFormat() const
|
|
||||||
{
|
|
||||||
return mDepthFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::setAutoUseNormalMaps(bool use)
|
void SceneManager::setAutoUseNormalMaps(bool use)
|
||||||
{
|
{
|
||||||
mAutoUseNormalMaps = use;
|
mAutoUseNormalMaps = use;
|
||||||
@ -440,9 +430,9 @@ namespace Resource
|
|||||||
mConvertAlphaTestToAlphaToCoverage = convert;
|
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()
|
SceneManager::~SceneManager()
|
||||||
@ -930,7 +920,8 @@ namespace Resource
|
|||||||
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
||||||
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
|
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
|
||||||
shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);
|
shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);
|
||||||
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex);
|
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex[0], mOpaqueDepthTex[1]);
|
||||||
|
shaderVisitor->setSupportsNormalsRT(mSupportsNormalsRT);
|
||||||
return shaderVisitor;
|
return shaderVisitor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,6 @@ namespace Resource
|
|||||||
void setClampLighting(bool clamp);
|
void setClampLighting(bool clamp);
|
||||||
bool getClampLighting() const;
|
bool getClampLighting() const;
|
||||||
|
|
||||||
void setDepthFormat(GLenum format);
|
|
||||||
GLenum getDepthFormat() const;
|
|
||||||
|
|
||||||
/// @see ShaderVisitor::setAutoUseNormalMaps
|
/// @see ShaderVisitor::setAutoUseNormalMaps
|
||||||
void setAutoUseNormalMaps(bool use);
|
void setAutoUseNormalMaps(bool use);
|
||||||
|
|
||||||
@ -112,12 +109,13 @@ namespace Resource
|
|||||||
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
|
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
|
||||||
bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;
|
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
|
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
|
// 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);
|
void setLightingMethod(SceneUtil::LightingMethod method);
|
||||||
SceneUtil::LightingMethod getLightingMethod() const;
|
SceneUtil::LightingMethod getLightingMethod() const;
|
||||||
@ -195,6 +193,9 @@ namespace Resource
|
|||||||
|
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
||||||
|
|
||||||
|
void setSupportsNormalsRT(bool supports) { mSupportsNormalsRT = supports; }
|
||||||
|
bool getSupportsNormalsRT() const { return mSupportsNormalsRT; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Shader::ShaderVisitor* createShaderVisitor(const std::string& shaderPrefix = "objects");
|
Shader::ShaderVisitor* createShaderVisitor(const std::string& shaderPrefix = "objects");
|
||||||
@ -211,8 +212,8 @@ namespace Resource
|
|||||||
SceneUtil::LightingMethod mLightingMethod;
|
SceneUtil::LightingMethod mLightingMethod;
|
||||||
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
|
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
|
||||||
bool mConvertAlphaTestToAlphaToCoverage;
|
bool mConvertAlphaTestToAlphaToCoverage;
|
||||||
GLenum mDepthFormat;
|
bool mSupportsNormalsRT;
|
||||||
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
|
std::array<osg::ref_ptr<osg::Texture2D>, 2> mOpaqueDepthTex;
|
||||||
|
|
||||||
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
|
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
|
||||||
mutable std::mutex mSharedStateMutex;
|
mutable std::mutex mSharedStateMutex;
|
||||||
|
42
components/sceneutil/clearcolor.hpp
Executable file
42
components/sceneutil/clearcolor.hpp
Executable 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
|
@ -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)
|
bool isDepthFormat(GLenum format)
|
||||||
{
|
{
|
||||||
constexpr std::array<GLenum, 8> formats = {
|
constexpr std::array<GLenum, 8> formats = {
|
||||||
|
@ -45,9 +45,6 @@ namespace SceneUtil
|
|||||||
// Returns an orthographic projection matrix for use with a reversed z-buffer.
|
// 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);
|
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
|
// Returns true if the GL format is a depth format
|
||||||
bool isDepthFormat(GLenum format);
|
bool isDepthFormat(GLenum format);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace SceneUtil
|
|||||||
, mSamples(samples)
|
, mSamples(samples)
|
||||||
, mGenerateMipmaps(generateMipmaps)
|
, mGenerateMipmaps(generateMipmaps)
|
||||||
, mColorBufferInternalFormat(Color::colorInternalFormat())
|
, mColorBufferInternalFormat(Color::colorInternalFormat())
|
||||||
, mDepthBufferInternalFormat(AutoDepth::depthInternalFormat())
|
, mDepthBufferInternalFormat(SceneUtil::AutoDepth::depthInternalFormat())
|
||||||
, mRenderOrderNum(renderOrderNum)
|
, mRenderOrderNum(renderOrderNum)
|
||||||
, mStereoAwareness(stereoAwareness)
|
, mStereoAwareness(stereoAwareness)
|
||||||
{
|
{
|
||||||
|
@ -152,42 +152,6 @@ void GlowUpdater::setDuration(float duration)
|
|||||||
mDuration = 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 colourFromRGB(unsigned int clr)
|
||||||
{
|
{
|
||||||
osg::Vec4f colour(((clr >> 0) & 0xFF) / 255.0f,
|
osg::Vec4f colour(((clr >> 0) & 0xFF) / 255.0f,
|
||||||
|
64
components/serialization/osgyaml.hpp
Normal file
64
components/serialization/osgyaml.hpp
Normal 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
|
174
components/settings/shadermanager.hpp
Normal file
174
components/settings/shadermanager.hpp
Normal 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
|
@ -11,6 +11,7 @@
|
|||||||
#include <osg/Multisample>
|
#include <osg/Multisample>
|
||||||
#include <osg/Texture>
|
#include <osg/Texture>
|
||||||
#include <osg/ValueObject>
|
#include <osg/ValueObject>
|
||||||
|
#include <osg/Capability>
|
||||||
|
|
||||||
#include <osgParticle/ParticleSystem>
|
#include <osgParticle/ParticleSystem>
|
||||||
|
|
||||||
@ -28,6 +29,50 @@
|
|||||||
#include "removedalphafunc.hpp"
|
#include "removedalphafunc.hpp"
|
||||||
#include "shadermanager.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
|
namespace Shader
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -165,6 +210,7 @@ namespace Shader
|
|||||||
, mAutoUseSpecularMaps(false)
|
, mAutoUseSpecularMaps(false)
|
||||||
, mApplyLightingToEnvMaps(false)
|
, mApplyLightingToEnvMaps(false)
|
||||||
, mConvertAlphaTestToAlphaToCoverage(false)
|
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||||
|
, mSupportsNormalsRT(false)
|
||||||
, mShaderManager(shaderManager)
|
, mShaderManager(shaderManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
, mDefaultShaderPrefix(defaultShaderPrefix)
|
, mDefaultShaderPrefix(defaultShaderPrefix)
|
||||||
@ -611,6 +657,14 @@ namespace Shader
|
|||||||
defineMap["endLight"] = "0";
|
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))
|
if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT && !previousAddedState->hasMode(GL_ALPHA_TEST))
|
||||||
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
|
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
|
||||||
// This disables the deprecated fixed-function alpha test
|
// This disables the deprecated fixed-function alpha test
|
||||||
@ -629,7 +683,7 @@ namespace Shader
|
|||||||
updateRemovedState(*writableUserData, removedState);
|
updateRemovedState(*writableUserData, removedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reqs.mSoftParticles)
|
if (reqs.mSoftParticles && mOpaqueDepthTex.front())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Depth> depth = new SceneUtil::AutoDepth;
|
osg::ref_ptr<osg::Depth> depth = new SceneUtil::AutoDepth;
|
||||||
depth->setWriteMask(false);
|
depth->setWriteMask(false);
|
||||||
@ -639,14 +693,18 @@ namespace Shader
|
|||||||
writableStateSet->addUniform(new osg::Uniform("particleSize", reqs.mSoftParticleSize));
|
writableStateSet->addUniform(new osg::Uniform("particleSize", reqs.mSoftParticleSize));
|
||||||
addedState->addUniform("particleSize");
|
addedState->addUniform("particleSize");
|
||||||
|
|
||||||
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", 2));
|
constexpr int unit = 2;
|
||||||
|
|
||||||
|
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", unit));
|
||||||
addedState->addUniform("opaqueDepthTex");
|
addedState->addUniform("opaqueDepthTex");
|
||||||
|
|
||||||
writableStateSet->setTextureAttributeAndModes(2, mOpaqueDepthTex, osg::StateAttribute::ON);
|
osg::ref_ptr<OpaqueDepthAttribute> opaqueDepthAttr = new OpaqueDepthAttribute;
|
||||||
addedState->setTextureAttributeAndModes(2, mOpaqueDepthTex);
|
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);
|
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||||
|
|
||||||
@ -840,7 +898,7 @@ namespace Shader
|
|||||||
{
|
{
|
||||||
pushRequirements(drawable);
|
pushRequirements(drawable);
|
||||||
|
|
||||||
if (partsys && mOpaqueDepthTex)
|
if (partsys)
|
||||||
{
|
{
|
||||||
mRequirements.back().mSoftParticles = true;
|
mRequirements.back().mSoftParticles = true;
|
||||||
mRequirements.back().mSoftParticleSize = partsys->getDefaultParticleTemplate().getSizeRange().maximum;
|
mRequirements.back().mSoftParticleSize = partsys->getDefaultParticleTemplate().getSizeRange().maximum;
|
||||||
@ -915,9 +973,9 @@ namespace Shader
|
|||||||
mConvertAlphaTestToAlphaToCoverage = convert;
|
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)
|
ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef OPENMW_COMPONENTS_SHADERVISITOR_H
|
#ifndef OPENMW_COMPONENTS_SHADERVISITOR_H
|
||||||
#define OPENMW_COMPONENTS_SHADERVISITOR_H
|
#define OPENMW_COMPONENTS_SHADERVISITOR_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include <osg/NodeVisitor>
|
#include <osg/NodeVisitor>
|
||||||
#include <osg/Program>
|
#include <osg/Program>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
@ -46,7 +48,9 @@ namespace Shader
|
|||||||
|
|
||||||
void setConvertAlphaTestToAlphaToCoverage(bool convert);
|
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;
|
void apply(osg::Node& node) override;
|
||||||
|
|
||||||
@ -73,6 +77,8 @@ namespace Shader
|
|||||||
|
|
||||||
bool mConvertAlphaTestToAlphaToCoverage;
|
bool mConvertAlphaTestToAlphaToCoverage;
|
||||||
|
|
||||||
|
bool mSupportsNormalsRT;
|
||||||
|
|
||||||
ShaderManager& mShaderManager;
|
ShaderManager& mShaderManager;
|
||||||
Resource::ImageManager& mImageManager;
|
Resource::ImageManager& mImageManager;
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ namespace Shader
|
|||||||
bool mShaderRequired;
|
bool mShaderRequired;
|
||||||
|
|
||||||
int mColorMode;
|
int mColorMode;
|
||||||
|
|
||||||
bool mMaterialOverridden;
|
bool mMaterialOverridden;
|
||||||
bool mAlphaTestOverridden;
|
bool mAlphaTestOverridden;
|
||||||
bool mAlphaBlendOverridden;
|
bool mAlphaBlendOverridden;
|
||||||
@ -116,7 +122,7 @@ namespace Shader
|
|||||||
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
||||||
|
|
||||||
osg::ref_ptr<const osg::Program> mProgramTemplate;
|
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
|
class ReinstateRemovedStateVisitor : public osg::NodeVisitor
|
||||||
|
162
components/std140/ubo.hpp
Normal file
162
components/std140/ubo.hpp
Normal 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
|
@ -3,7 +3,6 @@
|
|||||||
#include <osg/FrameBufferObject>
|
#include <osg/FrameBufferObject>
|
||||||
#include <osg/GLExtensions>
|
#include <osg/GLExtensions>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
#include <osg/Texture2DMultisample>
|
|
||||||
#include <osg/Texture2DArray>
|
#include <osg/Texture2DArray>
|
||||||
#include <osgUtil/RenderStage>
|
#include <osgUtil/RenderStage>
|
||||||
#include <osgUtil/CullVisitor>
|
#include <osgUtil/CullVisitor>
|
||||||
@ -322,10 +321,7 @@ namespace Stereo
|
|||||||
for (unsigned i = 0; i < 2; i++)
|
for (unsigned i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
if (mSamples > 1)
|
if (mSamples > 1)
|
||||||
{
|
mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples)));
|
||||||
mMsaaColorTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
|
|
||||||
mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mMsaaColorTexture[i]));
|
|
||||||
}
|
|
||||||
mColorTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
mColorTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
||||||
mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i]));
|
mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i]));
|
||||||
}
|
}
|
||||||
@ -351,10 +347,7 @@ namespace Stereo
|
|||||||
for (unsigned i = 0; i < 2; i++)
|
for (unsigned i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
if (mSamples > 1)
|
if (mSamples > 1)
|
||||||
{
|
mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples)));
|
||||||
mMsaaDepthTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
|
|
||||||
mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mMsaaDepthTexture[i]));
|
|
||||||
}
|
|
||||||
mDepthTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
mDepthTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
||||||
mLayerFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i]));
|
mLayerFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i]));
|
||||||
}
|
}
|
||||||
@ -381,6 +374,11 @@ namespace Stereo
|
|||||||
return mMultiviewColorTexture;
|
return mMultiviewColorTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Texture2DArray* MultiviewFramebuffer::multiviewDepthBuffer()
|
||||||
|
{
|
||||||
|
return mMultiviewDepthTexture;
|
||||||
|
}
|
||||||
|
|
||||||
osg::Texture2D* MultiviewFramebuffer::layerColorBuffer(int i)
|
osg::Texture2D* MultiviewFramebuffer::layerColorBuffer(int i)
|
||||||
{
|
{
|
||||||
return mColorTexture[i];
|
return mColorTexture[i];
|
||||||
@ -451,22 +449,6 @@ namespace Stereo
|
|||||||
return texture;
|
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* MultiviewFramebuffer::createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||||
{
|
{
|
||||||
osg::Texture2DArray* textureArray = new osg::Texture2DArray;
|
osg::Texture2DArray* textureArray = new osg::Texture2DArray;
|
||||||
|
@ -13,7 +13,6 @@ namespace osg
|
|||||||
class FrameBufferObject;
|
class FrameBufferObject;
|
||||||
class Texture;
|
class Texture;
|
||||||
class Texture2D;
|
class Texture2D;
|
||||||
class Texture2DMultisample;
|
|
||||||
class Texture2DArray;
|
class Texture2DArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +49,7 @@ namespace Stereo
|
|||||||
osg::FrameBufferObject* layerFbo(int i);
|
osg::FrameBufferObject* layerFbo(int i);
|
||||||
osg::FrameBufferObject* layerMsaaFbo(int i);
|
osg::FrameBufferObject* layerMsaaFbo(int i);
|
||||||
osg::Texture2DArray* multiviewColorBuffer();
|
osg::Texture2DArray* multiviewColorBuffer();
|
||||||
|
osg::Texture2DArray* multiviewDepthBuffer();
|
||||||
osg::Texture2D* layerColorBuffer(int i);
|
osg::Texture2D* layerColorBuffer(int i);
|
||||||
osg::Texture2D* layerDepthBuffer(int i);
|
osg::Texture2D* layerDepthBuffer(int i);
|
||||||
|
|
||||||
@ -62,7 +62,6 @@ namespace Stereo
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
osg::Texture2D* createTexture(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
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);
|
osg::Texture2DArray* createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||||
|
|
||||||
int mWidth;
|
int mWidth;
|
||||||
@ -76,9 +75,7 @@ namespace Stereo
|
|||||||
osg::ref_ptr<osg::Texture2DArray> mMultiviewColorTexture;
|
osg::ref_ptr<osg::Texture2DArray> mMultiviewColorTexture;
|
||||||
osg::ref_ptr<osg::Texture2DArray> mMultiviewDepthTexture;
|
osg::ref_ptr<osg::Texture2DArray> mMultiviewDepthTexture;
|
||||||
std::array<osg::ref_ptr<osg::Texture2D>, 2> mColorTexture;
|
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::Texture2D>, 2> mDepthTexture;
|
||||||
std::array<osg::ref_ptr<osg::Texture2DMultisample>, 2> mMsaaDepthTexture;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ namespace osg
|
|||||||
{
|
{
|
||||||
class FrameBufferObject;
|
class FrameBufferObject;
|
||||||
class Texture2D;
|
class Texture2D;
|
||||||
class Texture2DMultisample;
|
|
||||||
class Texture2DArray;
|
class Texture2DArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ std::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunk
|
|||||||
|
|
||||||
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
|
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)
|
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.mDiffuseMap = compositeMap->mTexture;
|
||||||
layer.mParallax = false;
|
layer.mParallax = false;
|
||||||
layer.mSpecular = 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
|
else
|
||||||
{
|
{
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
#include <osg/TexMat>
|
#include <osg/TexMat>
|
||||||
#include <osg/BlendFunc>
|
#include <osg/BlendFunc>
|
||||||
|
#include <osg/Capability>
|
||||||
|
|
||||||
#include <components/stereo/stereomanager.hpp>
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/shader/shadermanager.hpp>
|
#include <components/shader/shadermanager.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
|
||||||
@ -194,9 +196,10 @@ namespace
|
|||||||
|
|
||||||
namespace Terrain
|
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)
|
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;
|
std::vector<osg::ref_ptr<osg::StateSet> > passes;
|
||||||
|
|
||||||
unsigned int blendmapIndex = 0;
|
unsigned int blendmapIndex = 0;
|
||||||
@ -209,6 +212,8 @@ namespace Terrain
|
|||||||
if (!blendmaps.empty())
|
if (!blendmaps.empty())
|
||||||
{
|
{
|
||||||
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||||
|
if (sceneManager->getSupportsNormalsRT())
|
||||||
|
stateset->setAttribute(new osg::Disablei(GL_BLEND, 1));
|
||||||
stateset->setRenderBinDetails(firstLayer ? 0 : 1, "RenderBin");
|
stateset->setRenderBinDetails(firstLayer ? 0 : 1, "RenderBin");
|
||||||
if (!firstLayer)
|
if (!firstLayer)
|
||||||
{
|
{
|
||||||
@ -251,18 +256,18 @@ namespace Terrain
|
|||||||
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
||||||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
||||||
|
defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0";
|
||||||
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
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> 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> fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
||||||
if (!vertexShader || !fragmentShader)
|
if (!vertexShader || !fragmentShader)
|
||||||
{
|
{
|
||||||
// Try again without shader. Error already logged by above
|
// 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);
|
stateset->addUniform(UniformCollection::value().mColorMode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -10,9 +10,9 @@ namespace osg
|
|||||||
class Texture2D;
|
class Texture2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Shader
|
namespace Resource
|
||||||
{
|
{
|
||||||
class ShaderManager;
|
class SceneManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Terrain
|
namespace Terrain
|
||||||
@ -26,7 +26,7 @@ namespace Terrain
|
|||||||
bool mSpecular;
|
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<TextureLayer>& layers,
|
||||||
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
|
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ namespace VFS
|
|||||||
virtual ~File() {}
|
virtual ~File() {}
|
||||||
|
|
||||||
virtual Files::IStreamPtr open() = 0;
|
virtual Files::IStreamPtr open() = 0;
|
||||||
|
|
||||||
|
virtual std::string getPath() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Archive
|
class Archive
|
||||||
|
@ -15,6 +15,8 @@ namespace VFS
|
|||||||
|
|
||||||
Files::IStreamPtr open() override;
|
Files::IStreamPtr open() override;
|
||||||
|
|
||||||
|
std::string getPath() override { return mInfo->name(); }
|
||||||
|
|
||||||
const Bsa::BSAFile::FileStruct* mInfo;
|
const Bsa::BSAFile::FileStruct* mInfo;
|
||||||
Bsa::BSAFile* mFile;
|
Bsa::BSAFile* mFile;
|
||||||
};
|
};
|
||||||
@ -26,6 +28,8 @@ namespace VFS
|
|||||||
|
|
||||||
Files::IStreamPtr open() override;
|
Files::IStreamPtr open() override;
|
||||||
|
|
||||||
|
std::string getPath() override { return mInfo->name(); }
|
||||||
|
|
||||||
const Bsa::BSAFile::FileStruct* mInfo;
|
const Bsa::BSAFile::FileStruct* mInfo;
|
||||||
Bsa::CompressedBSAFile* mCompressedFile;
|
Bsa::CompressedBSAFile* mCompressedFile;
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,8 @@ namespace VFS
|
|||||||
|
|
||||||
Files::IStreamPtr open() override;
|
Files::IStreamPtr open() override;
|
||||||
|
|
||||||
|
std::string getPath() override { return mPath; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
|
|
||||||
|
@ -105,6 +105,17 @@ namespace VFS
|
|||||||
return {};
|
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
|
namespace
|
||||||
{
|
{
|
||||||
bool startsWith(std::string_view text, std::string_view start)
|
bool startsWith(std::string_view text, std::string_view start)
|
||||||
|
@ -90,6 +90,11 @@ namespace VFS
|
|||||||
/// @note May be called from any thread once the index has been built.
|
/// @note May be called from any thread once the index has been built.
|
||||||
RecursiveDirectoryRange getRecursiveDirectoryIterator(const std::string& path) const;
|
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:
|
private:
|
||||||
bool mStrict;
|
bool mStrict;
|
||||||
|
|
||||||
|
@ -7,4 +7,5 @@ Reference Material
|
|||||||
|
|
||||||
modding/index
|
modding/index
|
||||||
lua-scripting/index
|
lua-scripting/index
|
||||||
|
postprocessing/index
|
||||||
documentationHowTo
|
documentationHowTo
|
||||||
|
@ -20,6 +20,7 @@ Lua API reference
|
|||||||
openmw_input
|
openmw_input
|
||||||
openmw_ui
|
openmw_ui
|
||||||
openmw_camera
|
openmw_camera
|
||||||
|
openmw_postprocessing
|
||||||
openmw_aux_calendar
|
openmw_aux_calendar
|
||||||
openmw_aux_util
|
openmw_aux_util
|
||||||
openmw_aux_time
|
openmw_aux_time
|
||||||
@ -46,35 +47,37 @@ It can not be overloaded even if there is a lua file with the same name.
|
|||||||
The list of available packages is different for global and for local scripts.
|
The list of available packages is different for global and for local scripts.
|
||||||
Player scripts are local scripts that are attached to a player.
|
Player scripts are local scripts that are attached to a player.
|
||||||
|
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
| Package | Can be used | Description |
|
| Package | Can be used | Description |
|
||||||
+=========================================================+====================+===============================================================+
|
+============================================================+====================+===============================================================+
|
||||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||||
| | | | that don't depend on the game world. |
|
| | | | that don't depend on the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
||||||
| | | | between game sessions. |
|
| | | | between game sessions. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera. |
|
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|:ref:`openmw.postprocessing <Package openmw.postprocessing>`| by player scripts | | Controls post-process shaders. |
|
||||||
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|
||||||
**openmw_aux**
|
**openmw_aux**
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
Package openmw.postprocessing
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
:file: generated_html/openmw_postprocessing.html
|
@ -340,35 +340,37 @@ It can not be overloaded even if there is a lua file with the same name.
|
|||||||
The list of available packages is different for global and for local scripts.
|
The list of available packages is different for global and for local scripts.
|
||||||
Player scripts are local scripts that are attached to a player.
|
Player scripts are local scripts that are attached to a player.
|
||||||
|
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
| Package | Can be used | Description |
|
| Package | Can be used | Description |
|
||||||
+=========================================================+====================+===============================================================+
|
+============================================================+====================+===============================================================+
|
||||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||||
| | | | that don't depend on the game world. |
|
| | | | that don't depend on the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
||||||
| | | | between game sessions. |
|
| | | | between game sessions. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input |
|
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>` |
|
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>` |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera |
|
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera |
|
||||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|:ref:`openmw.postprocessing <Package openmw.postprocessing>`| by player scripts | | Controls postprocess shaders |
|
||||||
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|
||||||
openmw_aux
|
openmw_aux
|
||||||
----------
|
----------
|
||||||
|
@ -71,3 +71,4 @@ The ranges included with each setting are the physically possible ranges, not re
|
|||||||
navigator
|
navigator
|
||||||
physics
|
physics
|
||||||
models
|
models
|
||||||
|
postprocessing
|
||||||
|
65
docs/source/reference/modding/settings/postprocessing.rst
Normal file
65
docs/source/reference/modding/settings/postprocessing.rst
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
Post Processing Settings
|
||||||
|
########################
|
||||||
|
|
||||||
|
enabled
|
||||||
|
-------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
Enable or disable post processing.
|
||||||
|
This enables use of post processing shaders, which must be installed.
|
||||||
|
|
||||||
|
chain
|
||||||
|
-----
|
||||||
|
|
||||||
|
:Type: string list
|
||||||
|
|
||||||
|
Controls which post process effects are active and their order.
|
||||||
|
It is recommended to configure the settings and order of shaders through the in game HUD. By default this is available with the F2 key.
|
||||||
|
Note, an empty chain will not disable post processing.
|
||||||
|
|
||||||
|
This setting has no effect if :ref:`enabled` is set to false.
|
||||||
|
|
||||||
|
live reload
|
||||||
|
-----------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
Automatically reloads a shader if the file has been changed. This is useful for debugging and writing shaders yourself.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This should be disabled for normal gameplay
|
||||||
|
|
||||||
|
hdr exposure time
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
:Type: float
|
||||||
|
:Range: 0.0 to 1.0
|
||||||
|
:Default: 0.05
|
||||||
|
|
||||||
|
Use for eye adaptation to control speed at which average scene luminance can change from one frame to the next.
|
||||||
|
Average scene luminance is used in some shader effects for features such as dynamic eye adaptation.
|
||||||
|
Smaller values will cause slower changes in scene luminance. This is most impactful when the brightness
|
||||||
|
drastically changes quickly, like when entering a dark cave or exiting an interior and looking into a bright sun.
|
||||||
|
|
||||||
|
This settings has no effect when HDR is disabled or :ref:`enabled` is set to false.
|
||||||
|
|
||||||
|
transparent postpass
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: True
|
||||||
|
|
||||||
|
Re-renders transparent objects with alpha-clipping forced with a fixed threshold. This is particularly important with vanilla content, where blended
|
||||||
|
objects usually have depth writes disabled and massive margins between the geometry and texture alpha.
|
||||||
|
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This can be quite costly with vanilla assets. For best performance it is recommended to use a mod replacer which
|
||||||
|
uses alpha tested foliage and disable this setting. Morrowind Optimizaton Patch is a great option.
|
||||||
|
If you are not using any shaders which utilize the depth buffer this setting should be disabled.
|
@ -256,7 +256,21 @@ settings
|
|||||||
w = 0.33
|
w = 0.33
|
||||||
|
|
||||||
h = 0.66
|
h = 0.66
|
||||||
|
|
||||||
The settings window.
|
The settings window.
|
||||||
Activated by clicking Options in the main menu.
|
Activated by clicking Options in the main menu.
|
||||||
|
|
||||||
|
postprocessor
|
||||||
|
-------------
|
||||||
|
|
||||||
|
:Default:
|
||||||
|
x = 0.01
|
||||||
|
|
||||||
|
y = 0.02
|
||||||
|
|
||||||
|
w = 0.44
|
||||||
|
|
||||||
|
h = 0.95
|
||||||
|
|
||||||
|
The postprocessor window used to configure shaders.
|
||||||
|
Activated by pressing the F2 key.
|
||||||
|
11
docs/source/reference/postprocessing/index.rst
Normal file
11
docs/source/reference/postprocessing/index.rst
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
######################
|
||||||
|
OpenMW Post Processing
|
||||||
|
######################
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: Table of Contents
|
||||||
|
:includehidden:
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
overview
|
||||||
|
omwfx
|
862
docs/source/reference/postprocessing/omwfx.rst
Normal file
862
docs/source/reference/postprocessing/omwfx.rst
Normal file
@ -0,0 +1,862 @@
|
|||||||
|
#########################
|
||||||
|
OMWFX Language Reference
|
||||||
|
#########################
|
||||||
|
|
||||||
|
Overview
|
||||||
|
########
|
||||||
|
|
||||||
|
Shaders are written in a OpenMW specific ``*.omwfx`` format. This is a light
|
||||||
|
wrapper around GLSL, so a basic understanding of GLSL should be acquired before
|
||||||
|
attempting to write any shaders. Every shader must be contained within a single
|
||||||
|
``*.omwfx`` file, ``#include`` directives are currently unsupported.
|
||||||
|
|
||||||
|
By default, all shaders only guarantee support of GLSL 120 features. To target a
|
||||||
|
newer GLSL version, you must specify it in the `technique`_ block properties. If
|
||||||
|
the specified version is not supported on the target machine, the shader will
|
||||||
|
not load.
|
||||||
|
|
||||||
|
Reserved Keywords
|
||||||
|
#################
|
||||||
|
|
||||||
|
GLSL doesn't support namespaces, instead reserved prefixes are used. Do not
|
||||||
|
attempt to name anything starting with ``_`` or ``omw``, this will cause
|
||||||
|
name clashes.
|
||||||
|
|
||||||
|
|
||||||
|
Builtin Samplers
|
||||||
|
################
|
||||||
|
|
||||||
|
+------------------+---------------------------+---------------------------------------------+
|
||||||
|
| GLSL Type | Name | Description |
|
||||||
|
+==================+===========================+=============================================+
|
||||||
|
| sampler2D[Array] |``omw_SamplerLastShader`` | Color output of the last shader |
|
||||||
|
+------------------+---------------------------+---------------------------------------------+
|
||||||
|
| sampler2D[Array] |``omw_SamplerLastPass`` | Color output of the last pass |
|
||||||
|
+------------------+---------------------------+---------------------------------------------+
|
||||||
|
| sampler2D[Array] |``omw_SamplerDepth`` | Non-linear normalized depth |
|
||||||
|
+------------------+---------------------------+---------------------------------------------+
|
||||||
|
| sampler2D[Array] |``omw_SamplerNormals`` | Normalized world-space normals [0, 1] |
|
||||||
|
+------------------+---------------------------+---------------------------------------------+
|
||||||
|
|
||||||
|
These are included in a common header in every pass, they do not need to be re-defined.
|
||||||
|
It is recommended to use the accessor functions to retrieve the sampler value.
|
||||||
|
OpenMW supports multiview rendering, so these samplers will either be a
|
||||||
|
``sampler2D`` or ``sampler2DArray``. If you want more control over how you
|
||||||
|
sample textures, use the ``OMW_MULTIVIEW`` macro to determine the appropriate functions to use.
|
||||||
|
|
||||||
|
|
||||||
|
Builtin Uniforms
|
||||||
|
################
|
||||||
|
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| GLSL Type | Name | Description |
|
||||||
|
+=============+==============================+==================================================+
|
||||||
|
| mat4 | ``omw.projectionMatrix`` | The camera's projection matrix |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| mat4 | ``omw.invProjectionMatrix`` | The inverse of the camera's projection matrix |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| mat4 | ``omw.viewMatrix`` | The camera's view matrix |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| mat4 | ``omw.prevViewMatrix`` | The camera's previous frame view matrix |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| mat4 | ``omw.invViewMatrix`` | The inverse of the camera's view matrix |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec4 | ``omw.eyePos`` | The camera's eye position |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec4 | ``omw.eyeVec`` | The normalized camera's eye vector |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec4 | ``omw.fogColor`` | The RGBA color of fog |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec4 | ``omw.sunColor`` | The RGBA color of sun |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec4 | ``omw.sunPos`` | The normalized sun direction |
|
||||||
|
| | | |
|
||||||
|
| | | When the sun is set `omw.sunpos.z` is negated |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec2 | ``omw.resolution`` | The render target's resolution |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec2 | ``omw.rcpResolution`` | Reciprocal of the render target resolution |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| vec2 | ``omw.fogNear`` | The units at which the fog begins to render |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.fogFar`` | The units at which the fog ends |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.near`` | The camera's near clip |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.far`` | The camera's far clip |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.gameHour`` | The game hour in range [0,24) |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.sunVis`` | The sun's visibility between [0, 1] |
|
||||||
|
| | | |
|
||||||
|
| | | Influenced by types of weather |
|
||||||
|
| | | |
|
||||||
|
| | | Closer to zero during overcast weathers |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.waterHeight`` | The water height of current cell |
|
||||||
|
| | | |
|
||||||
|
| | | Exterior water level is always zero |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.simulationTime`` | The time in milliseconds since simulation began |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.deltaSimulationTime`` | The change in `omw.simulationTime` |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.windSpeed`` | The current wind speed |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| float | ``omw.weatherTransition`` | The transition factor between weathers [0, 1] |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| int | ``omw.weatherID`` | The current weather ID |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| int | ``omw.nextWeatherID`` | The next weather ID |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| bool | ``omw.isUnderwater`` | True if player is submerged underwater |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
| bool | ``omw.isInterior`` | True if player is in an interior |
|
||||||
|
| | | |
|
||||||
|
| | | False for interiors that behave like exteriors |
|
||||||
|
+-------------+------------------------------+--------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
Builtin Macros
|
||||||
|
##############
|
||||||
|
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
| Macro | Definition | Description |
|
||||||
|
+==================+================+===========================================================================+
|
||||||
|
|``OMW_REVERSE_Z`` | ``0`` or ``1`` | Whether a reversed depth buffer is in use. |
|
||||||
|
| | | |
|
||||||
|
| | | ``0`` Depth sampler will be in range [1, 0] |
|
||||||
|
| | | |
|
||||||
|
| | | ``1`` Depth sampler will be in range [0, 1] |
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
|``OMW_RADIAL_FOG``| ``0`` or ``1`` | Whether radial fog is in use. |
|
||||||
|
| | | |
|
||||||
|
| | | ``0`` Fog is linear |
|
||||||
|
| | | |
|
||||||
|
| | | ``1`` Fog is radial |
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
| ``OMW_HDR`` | ``0`` or ``1`` | Whether average scene luminance is computed every frame. |
|
||||||
|
| | | |
|
||||||
|
| | | ``0`` Average scene luminance is not computed |
|
||||||
|
| | | |
|
||||||
|
| | | ``1`` Average scene luminance is computed |
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
| ``OMW_NORMALS`` | ``0`` or ``1`` | Whether normals are available as a sampler in the technique. |
|
||||||
|
| | | |
|
||||||
|
| | | ``0`` Normals are not available |
|
||||||
|
| | | |
|
||||||
|
| | | ``1`` Normals are available. |
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
| ``OMW_MULTIVIEW``| ``0`` or ``1`` | Whether multiview rendering is in use. |
|
||||||
|
| | | |
|
||||||
|
| | | ``0`` Multiview not in use |
|
||||||
|
| | | |
|
||||||
|
| | | ``1`` Multiview in use. |
|
||||||
|
+------------------+----------------+---------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
Builtin Functions
|
||||||
|
#################
|
||||||
|
|
||||||
|
The following functions can be accessed in any fragment or vertex shader.
|
||||||
|
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| Function | Description |
|
||||||
|
+========================================+===============================================================================+
|
||||||
|
| ``float omw_GetDepth(vec2)`` | Returns the depth value from a sampler given a uv coordinate. |
|
||||||
|
| | |
|
||||||
|
| | Reverses sampled value when ``OMW_REVERSE_Z`` is set. |
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| ``float omw_GetEyeAdaptation()`` | Returns the average scene luminance in range [0, 1]. |
|
||||||
|
| | |
|
||||||
|
| | If HDR is not in use, this returns `1.0` |
|
||||||
|
| | |
|
||||||
|
| | Scene luminance is always calculated on original scene texture. |
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| ``vec4 omw_GetDepth(vec2 uv)`` | Returns non-linear normalized depth |
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| ``vec4 omw_GetLastShader(vec2 uv)`` | Returns RGBA color output of the last shader |
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| ``vec4 omw_GetLastPass(vec2 uv)`` | Returns RGBA color output of the last pass |
|
||||||
|
+----------------------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| ``vec3 omw_GetNormals(vec2 uv)`` | Returns normalized worldspace normals [-1, 1] |
|
||||||
|
| | |
|
||||||
|
| | The values in sampler are in [0, 1] but are transformed to [-1, 1] |
|
||||||
|
+----------------------------------------+-----------------------+-------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
Special Attributes
|
||||||
|
##################
|
||||||
|
|
||||||
|
To maintain maximum compatability with future releases, OpenMW defines specific keywords, attributes, and functions for you to use. These should be used instead of their
|
||||||
|
GLSL equivalent. Refer to the table below to view these mappings.
|
||||||
|
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| .omwfx | Description |
|
||||||
|
+===================+=========================================================+
|
||||||
|
| omw_In | use in place of ``in`` and ``varying`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Out | use in place of ``out`` and ```varying`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Position | use in place of ``gl_Position`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Vertex | use in place of ``gl_Vertex`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Fragment | use in place of ``gl_FragData[*]`` and ``gl_FragColor``|
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Texture1D() | use in place of ``texture1D()`` or ``texture()`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Texture2D() | use in place of ``texture2D()`` or ``texture()`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
| omw_Texture3D() | use in place of ``texture3D()`` or ``texture()`` |
|
||||||
|
+-------------------+---------------------------------------------------------+
|
||||||
|
|
||||||
|
Blocks
|
||||||
|
######
|
||||||
|
|
||||||
|
``fragment``
|
||||||
|
*************
|
||||||
|
|
||||||
|
Declare your passes with ``fragment`` followed by a unique name. We will define the order of these passes later on.
|
||||||
|
Each ``fragment`` block must contain valid GLSL. Below is a simple example of defining two passes.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment pass {
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor = vec4(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment otherPass {
|
||||||
|
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor = omw_GetLastPass(omw_TexCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
``vertex``
|
||||||
|
***********
|
||||||
|
|
||||||
|
For every ``fragment`` block you declare, OpenMW generates a default vertex shader if you do not define one. This is used to draw the fullscreen triangle used in postprocessing.
|
||||||
|
This means you rarely need to use a custom vertex shader. Using a vertex shader can sometimes be useful when you need to do lots of complicated calculations that don't rely on pixel location.
|
||||||
|
The vertex shader only invocates on the `3` vertices of the fullscreen triangle.
|
||||||
|
Below is an example of passing a value through a custom vertex shader to the fragment shader.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
vertex pass {
|
||||||
|
#if OMW_USE_BINDINGS
|
||||||
|
omw_In vec2 omw_Vertex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform sampler2D noiseSampler;
|
||||||
|
|
||||||
|
omw_Out vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
// custom output from vertex shader
|
||||||
|
omw_Out float noise;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_Position = vec4(omw_Vertex.xy, 0.0, 1.0);
|
||||||
|
omw_TexCoord = omw_Position.xy * 0.5 + 0.5;
|
||||||
|
|
||||||
|
noise = sqrt(omw_Texture2D(noiseSampler, vec2(0.5, 0.5)).r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment pass {
|
||||||
|
omw_Out vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
// our custom output from the vertex shader is available
|
||||||
|
omw_Out float noise;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor = vec4(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
``technique``
|
||||||
|
*************
|
||||||
|
|
||||||
|
Exactly one ``technique`` block is required for every shader file. In this we define important traits like author, description, requirements, and flags.
|
||||||
|
|
||||||
|
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| Property | Type | Description |
|
||||||
|
+==================+====================+===================================================+
|
||||||
|
| passes | literal list | ``,`` separated list of pass names |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| version | string | Shader version that shows in HUD |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| description | string | Shader description that shows in HUD |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| author | string | Shader authors that shows in HUD |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| glsl_Version | integer | GLSL version |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| glsl_profile | string | GLSL profile, like ``compatability`` |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| glsl_extensions | literal list | ``,`` separated list of required GLSL extensions |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| hdr | boolean | Whether HDR eye adaptation is required. |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| pass_normals | boolean | Pass normals from the forward passes. |
|
||||||
|
| | | |
|
||||||
|
| | | If unsupported, `OMW_NORMALS` will be set to `0` |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
| flags | `SHADER_FLAG`_ | ``,`` separated list of shader flags |
|
||||||
|
+------------------+--------------------+---------------------------------------------------+
|
||||||
|
|
||||||
|
In the code snippet below, a shader is defined that requires GLSL `330`, HDR capatiblities, and is only enabled underwater in exteriors.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment dummy {
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor = vec4(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
technique {
|
||||||
|
passes = dummy;
|
||||||
|
glsl_version = 330;
|
||||||
|
hdr = true;
|
||||||
|
flags = disable_interiors | disable_abovewater;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
``sampler_*``
|
||||||
|
*************
|
||||||
|
|
||||||
|
Any texture in the VFS can be loaded by a shader. All passes within the technique will have access to this texture as a sampler.
|
||||||
|
OpenMW currently supports ``1D``, ``2D``, and ``3D`` texture samplers, cubemaps can not yet be loaded.
|
||||||
|
|
||||||
|
+-------------+
|
||||||
|
| Block |
|
||||||
|
+=============+
|
||||||
|
| sampler_1d |
|
||||||
|
+-------------+
|
||||||
|
| sampler_2d |
|
||||||
|
+-------------+
|
||||||
|
| sampler_3d |
|
||||||
|
+-------------+
|
||||||
|
|
||||||
|
The properites for a ``sampler_*`` block are as following.
|
||||||
|
The only required property for a texture is its ``source``.
|
||||||
|
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
| Property | Type |
|
||||||
|
+=======================+=======================+
|
||||||
|
|``source`` | string |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``min_filter`` | `FILTER_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``mag_filter`` | `FILTER_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``wrap_s`` | `WRAP_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``wrap_t`` | `WRAP_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``wrap_r`` | `WRAP_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``compression`` | `COMPRESSION_MODE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``source_format`` | `SOURCE_FORMAT`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``source_type`` | `SOURCE_TYPE`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|``internal_format`` | `INTERNAL_FORMAT`_ |
|
||||||
|
+-----------------------+-----------------------+
|
||||||
|
|
||||||
|
In the code snippet below, a simple noise texture is loaded with nearest filtering.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
sampler_2d noise {
|
||||||
|
source = "textures/noise.png";
|
||||||
|
mag_filter = nearest;
|
||||||
|
min_filter = nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
To use the sampler, define the appropriately named `sampler2D` in any of your passes.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment pass {
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
uniform sampler2D noise;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
vec4 noise = omw_Texture2D(noise, omw_TexCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
``uniform_*``
|
||||||
|
**************
|
||||||
|
|
||||||
|
It is possible to define settings for your shaders that can be adjusted by either users or a Lua script.
|
||||||
|
|
||||||
|
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
| Block | default | min | max | static | step | description | header |
|
||||||
|
+=================+==========+==========+==========+=========+==========+==============+=========+
|
||||||
|
|``uniform_bool`` | boolean | x | x | boolean | x | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|``uniform_float``| float | float | float | boolean | float | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|``uniform_int`` | integer | integer | integer | boolean | integer | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|``uniform_vec2`` | vec2 | vec2 | vec2 | boolean | vec2 | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|``uniform_vec3`` | vec3 | vec3 | vec3 | boolean | vec3 | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|``uniform_vec4`` | vec4 | vec4 | vec4 | boolean | vec4 | string | string |
|
||||||
|
+-----------------+----------+----------+----------+---------+----------+--------------+---------+
|
||||||
|
|
||||||
|
The ``description`` field is used to display a toolip when viewed in the in-game HUD. The ``header`` field
|
||||||
|
field can be used to organize uniforms into groups in the HUD.
|
||||||
|
|
||||||
|
If you would like a uniform to be adjustable with Lua API you `must` set ``static = false;``. Doing this
|
||||||
|
will also remove the uniform from the players HUD.
|
||||||
|
|
||||||
|
Below is an example of declaring a ``vec3`` uniform.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
uniform_vec3 uColor {
|
||||||
|
default = vec3(0,1,1);
|
||||||
|
min = vec3(0,0,0);
|
||||||
|
max = vec3(1,1,1);
|
||||||
|
step = vec3(0.1, 0.1, 0.1);
|
||||||
|
description = "Color uniform";
|
||||||
|
static = true;
|
||||||
|
header = "Colors";
|
||||||
|
}
|
||||||
|
|
||||||
|
To use the uniform you can reference it in any pass, it should **not** be declared with the ``uniform`` keyword.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment pass {
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
vec3 color = uColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
``render_target``
|
||||||
|
*****************
|
||||||
|
|
||||||
|
Normally when defining passes, OpenMW will take care of setting up all of the render targets. Sometimes, this behavior
|
||||||
|
is not wanted and you want a custom render target.
|
||||||
|
|
||||||
|
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| Property | Type | Description |
|
||||||
|
+==================+=====================+=============================================================================+
|
||||||
|
| min_filter | `FILTER_MODE`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| mag_filter | `FILTER_MODE`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| wrap_s | `WRAP_MODE`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| wrap_t | `WRAP_MODE`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| internal_format | `INTERNAL_FORMAT`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| source_type | `SOURCE_TYPE`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| source_format | `SOURCE_FORMAT`_ | x |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| width_ratio | float | Automatic width as a percentage of screen width |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| height_ratio | float | Automatic width as a percentage of screen height |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| width | float | Width in pixels |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| height | float | Height in pixels |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
| mipmaps | boolean | Whether mipmaps should be generated every frame |
|
||||||
|
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
To use the render target you must assign passes to it, along with any optional clear modes or custom blend modes.
|
||||||
|
|
||||||
|
In the code snippet below a rendertarget is used to draw the red cannel of a scene at half resolution.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
render_target RT_Downsample {
|
||||||
|
width_ratio = 0.5;
|
||||||
|
height_ratio = 0.5;
|
||||||
|
internal_format = r16f;
|
||||||
|
source_type = float;
|
||||||
|
source_format = red;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment downsample2x(target=RT_Downsample) {
|
||||||
|
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
omw_FragColor.r = omw_GetLastShader(omw_TexCoord).r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Now, if we ever run the `downsample2x` pass it will write to the target buffer instead of the default
|
||||||
|
one assigned by the engine.
|
||||||
|
|
||||||
|
To use the uniform you can reference it in any pass, it should **not** be declared with the ``uniform`` keyword.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment pass {
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
vec3 color = uColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple Example
|
||||||
|
##############
|
||||||
|
|
||||||
|
Let us go through a simple example in which we apply a simple desaturation
|
||||||
|
filter with a user-configurable factor.
|
||||||
|
|
||||||
|
Our first step is defining our user-configurable variable. In this case all we
|
||||||
|
want is a normalized value between 0 and 1 to influence the amount of
|
||||||
|
desaturation to apply to the scene. Here we setup a new variable of type
|
||||||
|
``float``, define a few basic properties, and give it a tooltip description.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
uniform_float uDesaturationFactor {
|
||||||
|
default = 0.5;
|
||||||
|
min = 0.0;
|
||||||
|
max = 1.0;
|
||||||
|
step = 0.05;
|
||||||
|
static = true;
|
||||||
|
description = "Desaturation factor. A value of 1.0 is full grayscale.";
|
||||||
|
}
|
||||||
|
|
||||||
|
Now, we can setup our first pass. Remember a pass is just a pixel shader invocation.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
fragment desaturate {
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// fetch scene texture from last shader
|
||||||
|
vec4 scene = omw_GetLastShader(omw_TexCoord);
|
||||||
|
|
||||||
|
// desaturate RGB component
|
||||||
|
const vec3 luminance = vec3(0.299, 0.587, 0.144);
|
||||||
|
float gray = dot(luminance, scene.rgb);
|
||||||
|
|
||||||
|
omw_FragColor = vec4(mix(scene.rgb, vec3(gray), uDesaturationFactor), scene.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Next we can define our ``technique`` block, which is in charge of glueing
|
||||||
|
together passes, setting up metadata, and setting up various flags.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
technique {
|
||||||
|
description = "Desaturates scene";
|
||||||
|
passes = desaturate;
|
||||||
|
version = "1.0";
|
||||||
|
author = "Fargoth";
|
||||||
|
passes = desaturate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Putting it all together we have this simple shader.
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
uniform_float uDesaturationFactor {
|
||||||
|
default = 0.5;
|
||||||
|
min = 0.0;
|
||||||
|
max = 1.0;
|
||||||
|
step = 0.05;
|
||||||
|
description = "Desaturation factor. A value of 1.0 is full grayscale.";
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment desaturate {
|
||||||
|
omw_In vec2 omw_TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// fetch scene texture from last shader
|
||||||
|
vec4 scene = omw_GetLastShader(omw_TexCoord);
|
||||||
|
|
||||||
|
// desaturate RGB component
|
||||||
|
const vec3 luminance = vec3(0.299, 0.587, 0.144);
|
||||||
|
float gray = dot(luminance, scene.rgb);
|
||||||
|
|
||||||
|
omw_FragColor = vec4(mix(scene.rgb, vec3(gray), uDesaturationFactor), scene.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
technique {
|
||||||
|
description = "Desaturates scene";
|
||||||
|
passes = desaturate;
|
||||||
|
version = "1.0";
|
||||||
|
author = "Fargoth";
|
||||||
|
passes = desaturate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Types
|
||||||
|
#####
|
||||||
|
|
||||||
|
`SHADER_FLAG`
|
||||||
|
*************
|
||||||
|
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| Flag | Description |
|
||||||
|
+====================+==========================================================================+
|
||||||
|
| disable_interiors | Disable in interiors. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| disable_exteriors | Disable when in exteriors or interiors which behave like exteriors. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| disable_underwater | Disable when underwater. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| disable_abovewater | Disable when above water. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| disable_sunglare | Disables builtin sunglare. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
| hidden | Shader does not show in the HUD. Useful for shaders driven by Lua API. |
|
||||||
|
+--------------------+--------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
`BLEND_EQ`
|
||||||
|
**********
|
||||||
|
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+===================+===========================+
|
||||||
|
| rgba_min | GL_MIN |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| rgba_max | GL_MAX |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| alpha_min | GL_ALPHA_MIN_SGIX |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| alpha_max | GL_ALPHA_MAX_SGIX |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| logic_op | GL_LOGIC_OP |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| add | GL_FUNC_ADD |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| subtract | GL_FUNC_SUBTRACT |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
| reverse_subtract | GL_FUNC_REVERSE_SUBTRACT |
|
||||||
|
+-------------------+---------------------------+
|
||||||
|
|
||||||
|
`BLEND_FUNC`
|
||||||
|
************
|
||||||
|
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+===========================+==============================+
|
||||||
|
| dst_alpha | GL_DST_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| dst_color | GL_DST_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one | GL_ONE |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_dst_alpha | GL_ONE_MINUS_DST_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_dst_color | GL_ONE_MINUS_DST_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_src_alpha | GL_ONE_MINUS_SRC_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_src_color | GL_ONE_MINUS_SRC_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| src_alpha | GL_SRC_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| src_alpha_saturate | GL_SRC_ALPHA_SATURATE |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| src_color | GL_SRC_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| constant_color | GL_CONSTANT_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_constant_color | GL_ONE_MINUS_CONSTANT_COLOR |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| constant_alpha | GL_CONSTANT_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| one_minus_constant_alpha | GL_ONE_MINUS_CONSTANT_ALPHA |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
| zero | GL_ZERO |
|
||||||
|
+---------------------------+------------------------------+
|
||||||
|
|
||||||
|
`INTERNAL_FORMAT`
|
||||||
|
*****************
|
||||||
|
|
||||||
|
+--------------------+-----------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+====================+=======================+
|
||||||
|
| 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 |
|
||||||
|
+--------------------+-----------------------+
|
||||||
|
|
||||||
|
`SOURCE_TYPE`
|
||||||
|
*************
|
||||||
|
|
||||||
|
+--------------------+-----------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+====================+=======================+
|
||||||
|
| 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 |
|
||||||
|
+--------------------+-----------------------+
|
||||||
|
|
||||||
|
|
||||||
|
`SOURCE_FORMAT`
|
||||||
|
***************
|
||||||
|
|
||||||
|
+---------+---------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+=========+=========+
|
||||||
|
| red | GL_RED |
|
||||||
|
+---------+---------+
|
||||||
|
| rg | GL_RG |
|
||||||
|
+---------+---------+
|
||||||
|
| rgb | GL_RGB |
|
||||||
|
+---------+---------+
|
||||||
|
| bgr | GL_BGR |
|
||||||
|
+---------+---------+
|
||||||
|
| rgba | GL_RGBA |
|
||||||
|
+---------+---------+
|
||||||
|
| bgra | GL_BGRA |
|
||||||
|
+---------+---------+
|
||||||
|
|
||||||
|
`FILTER_MODE`
|
||||||
|
*************
|
||||||
|
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+=========================+============================+
|
||||||
|
| linear | GL_LINEAR |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| linear_mipmap_linear | GL_LINEAR_MIPMAP_LINEAR |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| linear_mipmap_nearest | GL_LINEAR_MIPMAP_NEAREST |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| nearest | GL_NEAREST |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| nearest_mipmap_linear | GL_NEAREST_MIPMAP_LINEAR |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
| nearest_mipmap_nearest | GL_NEAREST_MIPMAP_NEAREST |
|
||||||
|
+-------------------------+----------------------------+
|
||||||
|
|
||||||
|
`WRAP_MODE`
|
||||||
|
***********
|
||||||
|
|
||||||
|
+------------------+---------------------+
|
||||||
|
| .omwfx | OpenGL |
|
||||||
|
+==================+=====================+
|
||||||
|
| clamp | GL_CLAMP |
|
||||||
|
+------------------+---------------------+
|
||||||
|
| clamp_to_edge | GL_CLAMP_TO_EDGE |
|
||||||
|
+------------------+---------------------+
|
||||||
|
| clamp_to_border | GL_CLAMP_TO_BORDER |
|
||||||
|
+------------------+---------------------+
|
||||||
|
| repeat | GL_REPEAT |
|
||||||
|
+------------------+---------------------+
|
||||||
|
| mirror | GL_MIRRORED_REPEAT |
|
||||||
|
+------------------+---------------------+
|
||||||
|
|
||||||
|
`COMPRESSION_MODE`
|
||||||
|
******************
|
||||||
|
|
||||||
|
+-------------+
|
||||||
|
| .omwfx |
|
||||||
|
+=============+
|
||||||
|
| auto |
|
||||||
|
+-------------+
|
||||||
|
| arb |
|
||||||
|
+-------------+
|
||||||
|
| s3tc_dxt1 |
|
||||||
|
+-------------+
|
||||||
|
| s3tc_dxt3 |
|
||||||
|
+-------------+
|
||||||
|
| s3tc_dxt5 |
|
||||||
|
+-------------+
|
||||||
|
| pvrtc_2bpp |
|
||||||
|
+-------------+
|
||||||
|
| pvrtc_4bpp |
|
||||||
|
+-------------+
|
||||||
|
| etc |
|
||||||
|
+-------------+
|
||||||
|
| etc2 |
|
||||||
|
+-------------+
|
||||||
|
| rgtc1 |
|
||||||
|
+-------------+
|
||||||
|
| rgtc2 |
|
||||||
|
+-------------+
|
||||||
|
| s3tc_dxt1c |
|
||||||
|
+-------------+
|
||||||
|
| s3tc_dxt1a |
|
||||||
|
+-------------+
|
45
docs/source/reference/postprocessing/overview.rst
Normal file
45
docs/source/reference/postprocessing/overview.rst
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#####################################
|
||||||
|
Overview of Post Processing Framework
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
OpenMW supports a moddable post process framework for creating and
|
||||||
|
controlling screenspace effects. This is integrated into OpenMW's Lua API, see
|
||||||
|
`reference <../lua-scripting/openmw_shader.html>`_ for details.
|
||||||
|
|
||||||
|
Basic concepts
|
||||||
|
==============
|
||||||
|
|
||||||
|
Pass
|
||||||
|
Describes a single shader invocation pass. Currently only pixel (also known
|
||||||
|
as fragment) shaders are supported.
|
||||||
|
|
||||||
|
Technique/Shader
|
||||||
|
An ordered list of passes, techniques will encompass a single effect like
|
||||||
|
bloom or SSAO. Technique is interchangable with shader.
|
||||||
|
|
||||||
|
Installing and Activating
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Shaders are managed through the virtual file system, simply install the associated
|
||||||
|
archive or folder as described in :ref:`mod-install<install>`. Shaders must be
|
||||||
|
in the `Shaders` directory to be discoverable. A shader can be activated in one
|
||||||
|
of two ways:
|
||||||
|
|
||||||
|
1. Adding the shaders filename (without its extension) to the end of the
|
||||||
|
:ref:`chain` list in ``settings.cfg``.
|
||||||
|
2. Using the in game post processor HUD, which can be activated with the ``F2``
|
||||||
|
key by default. This is the recommended method as manual editing can be error
|
||||||
|
prone.
|
||||||
|
|
||||||
|
Hot Reloading
|
||||||
|
==============
|
||||||
|
|
||||||
|
It is possible to modify a shader without restarting OpenMW, :ref:`live reload`
|
||||||
|
must be enabled in ``settings.cfg``. Whenever a file is modified and saved, the
|
||||||
|
shader will automatically reload in game. This allows shaders to be written in a
|
||||||
|
text editor you are comfortable with. The only restriction is that new shaders
|
||||||
|
cannot be added, as the VFS will not be rebuilt and OpenMW will not be aware of
|
||||||
|
the new file.
|
@ -13,6 +13,7 @@ set(LUA_API_FILES
|
|||||||
openmw/util.lua
|
openmw/util.lua
|
||||||
openmw/world.lua
|
openmw/world.lua
|
||||||
openmw/types.lua
|
openmw/types.lua
|
||||||
|
openmw/postprocessing.lua
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach (f ${LUA_API_FILES})
|
foreach (f ${LUA_API_FILES})
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user