1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-10 12:39:53 +00:00

Merge branch 'master' of gitlab.com:openmw/openmw into lua_class_data

This commit is contained in:
Zackhasacat 2023-10-25 21:05:35 -05:00
commit 909be9cf35
66 changed files with 469 additions and 346 deletions

View File

@ -531,6 +531,7 @@ macOS13_Xcode14_arm64:
- choco install ccache -y
- choco install vswhere -y
- choco install ninja -y
- choco install python -y
- choco install awscli -y
- refreshenv
- |
@ -590,7 +591,7 @@ macOS13_Xcode14_arm64:
- Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache:
key: ninja-v7
key: ninja-v8
paths:
- ccache
- deps
@ -652,6 +653,7 @@ macOS13_Xcode14_arm64:
- choco install 7zip -y
- choco install ccache -y
- choco install vswhere -y
- choco install python -y
- choco install awscli -y
- refreshenv
- |
@ -710,7 +712,7 @@ macOS13_Xcode14_arm64:
- Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache:
key: msbuild-v7
key: msbuild-v8
paths:
- ccache
- deps

View File

@ -4,7 +4,10 @@ sphinx:
configuration: docs/source/conf.py
python:
version: 3.8
install:
- requirements: docs/requirements.txt
build:
os: ubuntu-22.04
tools:
python: "3.8"

View File

@ -77,6 +77,7 @@
Bug #7603: Scripts menu size is not updated properly
Bug #7604: Goblins Grunt becomes idle once injured
Bug #7609: ForceGreeting should not open dialogue for werewolves
Bug #7611: Beast races' idle animations slide after turning or jumping in place
Bug #7630: Charm can be cast on creatures
Feature #3537: Shader-based water ripples
Feature #5492: Let rain and snow collide with statics
@ -108,6 +109,7 @@
Feature #7546: Start the game on Fredas
Feature #7568: Uninterruptable scripted music
Feature #7618: Show the player character's health in the save details
Feature #7634: Support NiParticleBomb
Task #5896: Do not use deprecated MyGUI properties
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector

View File

@ -1,3 +1,5 @@
$ErrorActionPreference = "Stop"
if (-Not (Test-Path CMakeCache.txt))
{
Write-Error "This script must be run from the build directory."
@ -8,6 +10,9 @@ if (-Not (Test-Path .cmake\api\v1\reply\index-*.json) -Or -Not ((Get-Content -Ra
Write-Output "Running CMake query..."
New-Item -Type File -Force .cmake\api\v1\query\codemodel-v2
cmake .
if ($LASTEXITCODE -ne 0) {
Write-Error "Command exited with code $LASTEXITCODE"
}
Write-Output "Done."
}
@ -45,13 +50,26 @@ finally
if (-not (Test-Path symstore-venv))
{
python -m venv symstore-venv
if ($LASTEXITCODE -ne 0) {
Write-Error "Command exited with code $LASTEXITCODE"
}
}
$symstoreVersion = "0.3.4"
if (-not (Test-Path symstore-venv\Scripts\symstore.exe) -or -not ((symstore-venv\Scripts\pip show symstore | Select-String '(?<=Version: ).*').Matches.Value -eq $symstoreVersion))
{
symstore-venv\Scripts\pip install symstore==$symstoreVersion
if ($LASTEXITCODE -ne 0) {
Write-Error "Command exited with code $LASTEXITCODE"
}
}
$artifacts = $artifacts | Where-Object { Test-Path $_ }
Write-Output "Storing symbols..."
symstore-venv\Scripts\symstore --compress --skip-published .\SymStore @artifacts
if ($LASTEXITCODE -ne 0) {
Write-Error "Command exited with code $LASTEXITCODE"
}
Write-Output "Done."

View File

@ -34,7 +34,7 @@ qmake --version
if [[ "${MACOS_AMD64}" ]]; then
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20221113.zip -o ~/openmw-deps.zip
else
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20230920_arm64.zip -o ~/openmw-deps.zip
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20231022_arm64.zip -o ~/openmw-deps.zip
fi
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null

View File

@ -597,14 +597,14 @@ if [ -z $SKIP_DOWNLOAD ]; then
"ffmpeg-${FFMPEG_VER}-dev-win${BITS}.zip"
# MyGUI
download "MyGUI 3.4.2" \
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z" \
"MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z"
download "MyGUI 3.4.3" \
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z" \
"MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z"
if [ -n "$PDBS" ]; then
download "MyGUI symbols" \
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z" \
"MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z"
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z" \
"MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z"
fi
# OpenAL
@ -768,20 +768,20 @@ printf "FFmpeg ${FFMPEG_VER}... "
}
cd $DEPS
echo
printf "MyGUI 3.4.2... "
printf "MyGUI 3.4.3... "
{
cd $DEPS_INSTALL
if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
grep "MYGUI_VERSION_PATCH 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z" $STRIP
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z" $STRIP
mv "MyGUI-3.4.2-msvc${MYGUI_MSVC_YEAR}-win${BITS}" MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}.7z" $STRIP
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}-sym.7z" $STRIP
mv "MyGUI-3.4.3-msvc${MYGUI_MSVC_YEAR}-win${BITS}" MyGUI
fi
export MYGUI_HOME="$(real_pwd)/MyGUI"
for CONFIGURATION in ${CONFIGURATIONS[@]}; do

View File

@ -122,4 +122,6 @@ mkdir -pv "$APT_CACHE_DIR"
apt-get update -yqq
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null
add-apt-repository -y ppa:openmw/openmw
add-apt-repository -y ppa:openmw/openmw-daily
add-apt-repository -y ppa:openmw/staging
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null

View File

@ -125,8 +125,8 @@ modules:
- "-DMYGUI_BUILD_PLUGINS=0"
sources:
- type: archive
url: https://github.com/MyGUI/mygui/archive/refs/tags/MyGUI3.4.2.tar.gz
sha256: 1cc45fb96c9438e3476778449af0378443d84794a458978a29c75306e45dd45a
url: https://github.com/MyGUI/mygui/archive/refs/tags/MyGUI3.4.3.tar.gz
sha256: 33c91b531993047e77cace36d6fea73634b8c17bd0ed193d4cd12ac7c6328abd
- name: libunshield
buildsystem: cmake-ninja

View File

@ -454,7 +454,7 @@ set(Boost_NO_WARN_NEW_VERSIONS ON) # ignore warnings about new releases of boos
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS})
if(OPENMW_USE_SYSTEM_MYGUI)
find_package(MyGUI 3.4.2 REQUIRED)
find_package(MyGUI 3.4.3 REQUIRED)
endif()
find_package(SDL2 2.0.9 REQUIRED)
find_package(OpenAL REQUIRED)

View File

@ -183,6 +183,8 @@ if (MSVC)
)
endif()
add_definitions(-DMYGUI_DONT_USE_OBSOLETE=ON)
if (ANDROID)
target_link_libraries(openmw EGL android log z)
endif (ANDROID)

View File

@ -229,7 +229,8 @@ namespace MWBase
virtual void unsetSelectedWeapon() = 0;
virtual void showCrosshair(bool show) = 0;
virtual bool toggleHud() = 0;
virtual bool setHudVisibility(bool show) = 0;
virtual bool isHudVisible() const = 0;
virtual void disallowMouse() = 0;
virtual void allowMouse() = 0;

View File

@ -23,8 +23,8 @@ namespace MWGui
MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator();
while (info.next("Property"))
{
const std::string& key = info->findAttribute("key");
const std::string& value = info->findAttribute("value");
auto key = info->findAttribute("key");
auto value = info->findAttribute("value");
if (key == "Point")
mPoint = MyGUI::IntPoint::parse(value);

View File

@ -475,7 +475,7 @@ namespace MWGui::Formatting
: GraphicElement(parent, pag, blockStyle)
, mTextStyle(textStyle)
{
Gui::EditBox* box = parent->createWidget<Gui::EditBox>("NormalText",
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + MyGUI::utility::toString(parent->getChildCount()));
box->setEditStatic(true);

View File

@ -161,7 +161,7 @@ namespace MWGui
private:
int currentFontHeight() const;
TextStyle mTextStyle;
Gui::EditBox* mEditBox;
MyGUI::EditBox* mEditBox;
};
class ImageElement : public GraphicElement

View File

@ -9,15 +9,14 @@ namespace MWGui
void resizeSkin(MyGUI::xml::ElementPtr _node)
{
_node->setAttribute("type", "ResourceSkin");
const std::string size = _node->findAttribute("size");
if (!size.empty())
if (!_node->findAttribute("size").empty())
return;
const std::string textureName = _node->findAttribute("texture");
auto textureName = _node->findAttribute("texture");
if (textureName.empty())
return;
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(textureName);
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(std::string{ textureName });
if (!texture)
return;
@ -30,7 +29,7 @@ namespace MWGui
if (basis->getName() != "BasisSkin")
continue;
const std::string basisSkinType = basis->findAttribute("type");
auto basisSkinType = basis->findAttribute("type");
if (Misc::StringUtils::ciEqual(basisSkinType, "SimpleText"))
continue;
bool isTileRect = Misc::StringUtils::ciEqual(basisSkinType, "TileRect");

View File

@ -168,7 +168,7 @@ namespace MWGui
std::string_view type = getSettingType(current);
if (type == checkButtonType)
{
const std::string initialValue
std::string_view initialValue
= Settings::get<bool>(getSettingCategory(current), getSettingName(current)) ? "#{Interface:On}"
: "#{Interface:Off}";
current->castType<MyGUI::Button>()->setCaptionWithReplacing(initialValue);
@ -242,12 +242,12 @@ namespace MWGui
void SettingsWindow::updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value)
{
std::string labelWidgetName = scroller->getUserString("SettingLabelWidget");
auto labelWidgetName = scroller->getUserString("SettingLabelWidget");
if (!labelWidgetName.empty())
{
MyGUI::TextBox* textBox;
getWidget(textBox, labelWidgetName);
std::string labelCaption = scroller->getUserString("SettingLabelCaption");
std::string labelCaption{ scroller->getUserString("SettingLabelCaption") };
labelCaption = Misc::StringUtils::format(labelCaption, value);
textBox->setCaptionWithReplacing(labelCaption);
}

View File

@ -238,7 +238,7 @@ namespace MWGui
mLines.emplace_back(separator, (MyGUI::Widget*)nullptr, NoSpellIndex);
}
MyGUI::TextBox* groupWidget = mScrollView->createWidget<Gui::TextBox>("SandBrightText",
MyGUI::TextBox* groupWidget = mScrollView->createWidget<MyGUI::TextBox>("SandBrightText",
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top);
groupWidget->setCaptionWithReplacing(label);
groupWidget->setTextAlign(MyGUI::Align::Left);
@ -246,7 +246,7 @@ namespace MWGui
if (!label2.empty())
{
MyGUI::TextBox* groupWidget2 = mScrollView->createWidget<Gui::TextBox>("SandBrightText",
MyGUI::TextBox* groupWidget2 = mScrollView->createWidget<MyGUI::TextBox>("SandBrightText",
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top);
groupWidget2->setCaptionWithReplacing(label2);
groupWidget2->setTextAlign(MyGUI::Align::Right);

View File

@ -244,7 +244,7 @@ namespace MWGui
= store->get<ESM::Skill>().find(MWMechanics::getSpellSchool(spell, player))->mSchool;
info.text = "#{sSchool}: " + MyGUI::TextIterator::toTagsString(school->mName).asUTF8();
}
const std::string& cost = focus->getUserString("SpellCost");
auto cost = focus->getUserString("SpellCost");
if (!cost.empty() && cost != "0")
info.text
+= MWGui::ToolTips::getValueString(MWMechanics::calcSpellCost(*spell), "#{sCastCost}");
@ -443,7 +443,7 @@ namespace MWGui
const std::string realImage
= Misc::ResourceHelpers::correctIconPath(image, MWBase::Environment::get().getResourceSystem()->getVFS());
Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(
MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>(
"NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
captionWidget->setEditStatic(true);
captionWidget->setNeedKeyFocus(false);
@ -452,7 +452,7 @@ namespace MWGui
int captionHeight = std::max(!caption.empty() ? captionSize.height : 0, imageSize);
Gui::EditBox* textWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>("SandText",
MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("SandText",
MyGUI::IntCoord(0, captionHeight + imageCaptionVPadding, 300, 300 - captionHeight - imageCaptionVPadding),
MyGUI::Align::Stretch, "ToolTipText");
textWidget->setEditStatic(true);
@ -474,7 +474,7 @@ namespace MWGui
MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget<MyGUI::ImageBox>("MarkerButton",
MyGUI::IntCoord(padding.left, totalSize.height + padding.top, 8, 8), MyGUI::Align::Default);
icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f));
Gui::EditBox* edit = mDynamicToolTipBox->createWidget<Gui::EditBox>("SandText",
MyGUI::EditBox* edit = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("SandText",
MyGUI::IntCoord(padding.left + 8 + 4, totalSize.height + padding.top, 300 - padding.left - 8 - 4,
300 - totalSize.height),
MyGUI::Align::Default);

View File

@ -1228,7 +1228,7 @@ namespace MWGui
MWBase::Environment::get().getStateManager()->requestQuit();
}
void WindowManager::onCursorChange(const std::string& name)
void WindowManager::onCursorChange(std::string_view name)
{
mCursorManager->cursorChanged(name);
}
@ -1605,9 +1605,9 @@ namespace MWGui
mQuickKeysMenu->activateQuickKey(index);
}
bool WindowManager::toggleHud()
bool WindowManager::setHudVisibility(bool show)
{
mHudEnabled = !mHudEnabled;
mHudEnabled = show;
updateVisible();
mMessageBoxManager->setVisible(mHudEnabled);
return mHudEnabled;
@ -2072,13 +2072,13 @@ namespace MWGui
mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f);
}
void WindowManager::onClipboardChanged(const std::string& _type, const std::string& _data)
void WindowManager::onClipboardChanged(std::string_view _type, std::string_view _data)
{
if (_type == "Text")
SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str());
}
void WindowManager::onClipboardRequested(const std::string& _type, std::string& _data)
void WindowManager::onClipboardRequested(std::string_view _type, std::string& _data)
{
if (_type != "Text")
return;
@ -2187,7 +2187,7 @@ namespace MWGui
ResourceImageSetPointerFix* imgSetPointer = resource->castType<ResourceImageSetPointerFix>(false);
if (!imgSetPointer)
continue;
std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0, 0).texture;
auto tex_name = imgSetPointer->getImageSet()->getIndexInfo(0, 0).texture;
osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(tex_name);

View File

@ -247,7 +247,8 @@ namespace MWGui
void showCrosshair(bool show) override;
/// Turn visibility of HUD on or off
bool toggleHud() override;
bool setHudVisibility(bool show) override;
bool isHudVisible() const override { return mHudEnabled; }
void disallowMouse() override;
void allowMouse() override;
@ -560,7 +561,7 @@ namespace MWGui
*/
void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result);
void onCursorChange(const std::string& name);
void onCursorChange(std::string_view name);
void onKeyFocusChanged(MyGUI::Widget* widget);
// Key pressed while playing a video
@ -568,8 +569,8 @@ namespace MWGui
void sizeVideo(int screenWidth, int screenHeight);
void onClipboardChanged(const std::string& _type, const std::string& _data);
void onClipboardRequested(const std::string& _type, std::string& _data);
void onClipboardChanged(std::string_view _type, std::string_view _data);
void onClipboardRequested(std::string_view _type, std::string& _data);
void createTextures();
void createCursors();

View File

@ -118,7 +118,7 @@ namespace MWInput
quickKey(10);
break;
case A_ToggleHUD:
windowManager->toggleHud();
windowManager->setHudVisibility(!windowManager->isHudVisible());
break;
case A_ToggleDebug:
windowManager->toggleDebugWindow();

View File

@ -7,6 +7,7 @@
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwscript/globalscripts.hpp"
#include "../mwworld/esmstore.hpp"
#include "object.hpp"
@ -43,6 +44,10 @@ namespace sol
struct is_automagical<MWLua::MWScriptVariables> : std::false_type
{
};
template <>
struct is_automagical<ESM::Global> : std::false_type
{
};
}
namespace MWLua
@ -76,6 +81,7 @@ namespace MWLua
// api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts
// api["getLocalScripts"] = [](const GObject& obj) -> list of scripts
sol::state_view& lua = context.mLua->sol();
sol::usertype<MWScriptRef> mwscript = context.mLua->sol().new_usertype<MWScriptRef>("MWScript");
sol::usertype<MWScriptVariables> mwscriptVars
= context.mLua->sol().new_usertype<MWScriptVariables>("MWScriptVariables");
@ -108,6 +114,51 @@ namespace MWLua
"No variable \"" + std::string(var) + "\" in mwscript " + s.mRef.mId.toDebugString());
};
using GlobalStore = MWWorld::Store<ESM::Global>;
sol::usertype<GlobalStore> globalStoreT = lua.new_usertype<GlobalStore>("ESM3_GlobalStore");
const GlobalStore* globalStore = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Global>();
globalStoreT[sol::meta_function::to_string] = [](const GlobalStore& store) {
return "ESM3_GlobalStore{" + std::to_string(store.getSize()) + " globals}";
};
globalStoreT[sol::meta_function::length] = [](const GlobalStore& store) { return store.getSize(); };
globalStoreT[sol::meta_function::index]
= sol::overload([](const GlobalStore& store, std::string_view globalId) -> sol::optional<float> {
auto g = store.search(ESM::RefId::deserializeText(globalId));
if (g == nullptr)
return sol::nullopt;
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
if (varType == 's' || varType == 'l')
{
return static_cast<float>(MWBase::Environment::get().getWorld()->getGlobalInt(globalId));
}
else
{
return MWBase::Environment::get().getWorld()->getGlobalFloat(globalId);
}
});
globalStoreT[sol::meta_function::new_index]
= sol::overload([](const GlobalStore& store, std::string_view globalId, float val) {
auto g = store.search(ESM::RefId::deserializeText(globalId));
if (g == nullptr)
return;
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
if (varType == 's' || varType == 'l')
{
MWBase::Environment::get().getWorld()->setGlobalInt(globalId, static_cast<int>(val));
}
else
{
MWBase::Environment::get().getWorld()->setGlobalFloat(globalId, val);
}
});
globalStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
globalStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
api["getGlobalVariables"] = [globalStore](sol::optional<GObject> player) {
if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("First argument must either be a player or be missing");
return globalStore;
};
return LuaUtil::makeReadOnly(api);
}

View File

@ -211,6 +211,13 @@ namespace MWLua
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrEmpty().isEmpty(); };
objectT["recordId"] = sol::readonly_property(
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
objectT["globalVariable"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<std::string> {
std::string globalVariable = o.ptr().getCellRef().getGlobalVariable();
if (globalVariable.empty())
return sol::nullopt;
else
return ESM::RefId::stringRefId(globalVariable).serializeText();
});
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
const MWWorld::Ptr& ptr = o.ptr();
MWWorld::WorldModel* wm = MWBase::Environment::get().getWorldModel();

View File

@ -111,6 +111,10 @@ namespace MWLua
};
sol::table api = context.mLua->newTable();
api["_setHudVisibility"] = [luaManager = context.mLuaManager](bool state) {
luaManager->addAction([state] { MWBase::Environment::get().getWindowManager()->setHudVisibility(state); });
};
api["_isHudVisible"] = []() -> bool { return MWBase::Environment::get().getWindowManager()->isHudVisible(); };
api["showMessage"]
= [luaManager = context.mLuaManager](std::string_view message) { luaManager->addUIMessage(message); };
api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string, Misc::Color>({
@ -296,7 +300,6 @@ namespace MWLua
};
// TODO
// api["_showHUD"] = [](bool) {};
// api["_showMouseCursor"] = [](bool) {};
return LuaUtil::makeReadOnly(api);

View File

@ -353,6 +353,7 @@ namespace MWMechanics
{
clearStateAnimation(mCurrentMovement);
mMovementState = CharState_None;
mMovementAnimationHasMovement = false;
}
void CharacterController::resetCurrentIdleState()
@ -705,7 +706,7 @@ namespace MWMechanics
if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement)
mAnimation->getInfo(mCurrentMovement, &startpoint);
mMovementAnimationControlled = true;
mMovementAnimationHasMovement = true;
clearStateAnimation(mCurrentMovement);
mCurrentMovement = movementAnimName;
@ -743,7 +744,7 @@ namespace MWMechanics
bool sneaking = mMovementState == CharState_SneakForward || mMovementState == CharState_SneakBack
|| mMovementState == CharState_SneakLeft || mMovementState == CharState_SneakRight;
mMovementAnimSpeed = (sneaking ? 33.5452f : (isRunning() ? 222.857f : 154.064f));
mMovementAnimationControlled = false;
mMovementAnimationHasMovement = false;
}
}
@ -852,7 +853,6 @@ namespace MWMechanics
resetCurrentHitState();
resetCurrentIdleState();
resetCurrentJumpState();
mMovementAnimationControlled = true;
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All, false, 1.0f, "start",
"stop", startpoint, 0);
@ -2309,9 +2309,6 @@ namespace MWMechanics
updateIdleStormState(inwater);
}
if (mInJump)
mMovementAnimationControlled = false;
if (isTurning())
{
// Adjust animation speed from 1.0 to 1.5 multiplier
@ -2347,7 +2344,7 @@ namespace MWMechanics
}
}
if (!mMovementAnimationControlled)
if (!isMovementAnimationControlled())
world->queueMovement(mPtr, vec);
}
@ -2416,7 +2413,7 @@ namespace MWMechanics
}
// Update movement
if (mMovementAnimationControlled && mPtr.getClass().isActor())
if (isMovementAnimationControlled() && mPtr.getClass().isActor())
world->queueMovement(mPtr, moved);
mSkipAnim = false;
@ -2577,6 +2574,16 @@ namespace MWMechanics
return mAnimation->isPlaying(groupName);
}
bool CharacterController::isMovementAnimationControlled() const
{
bool movementAnimationControlled = mIdleState != CharState_None;
if (mMovementState != CharState_None)
movementAnimationControlled = mMovementAnimationHasMovement;
if (mInJump)
movementAnimationControlled = false;
return movementAnimationControlled;
}
void CharacterController::clearAnimQueue(bool clearPersistAnims)
{
// Do not interrupt scripted animations, if we want to keep them

View File

@ -147,7 +147,7 @@ namespace MWMechanics
std::string mCurrentMovement;
float mMovementAnimSpeed{ 0.f };
bool mAdjustMovementAnimSpeed{ false };
bool mMovementAnimationControlled{ true };
bool mMovementAnimationHasMovement{ false };
CharacterState mDeathState{ CharState_None };
std::string mCurrentDeath;
@ -216,6 +216,7 @@ namespace MWMechanics
static bool isRandomAttackAnimation(std::string_view group);
bool isPersistentAnimPlaying() const;
bool isMovementAnimationControlled() const;
void updateAnimQueue();

View File

@ -192,7 +192,8 @@ namespace MWScript
public:
void execute(Interpreter::Runtime& runtime) override
{
bool state = MWBase::Environment::get().getWindowManager()->toggleHud();
bool state = MWBase::Environment::get().getWindowManager()->setHudVisibility(
!MWBase::Environment::get().getWindowManager()->isHudVisible());
runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off");
if (!state)

View File

@ -287,6 +287,7 @@ add_component_dir (debug
debugging debuglog gldebug debugdraw writeflags
)
add_definitions(-DMYGUI_DONT_USE_OBSOLETE=ON)
IF(NOT WIN32 AND NOT APPLE)
add_definitions(-DGLOBAL_DATA_PATH="${GLOBAL_DATA_PATH}")
add_definitions(-DGLOBAL_CONFIG_PATH="${GLOBAL_CONFIG_PATH}")

View File

@ -58,7 +58,7 @@ namespace
MyGUI::xml::ElementPtr sizeProperty = getProperty(layersIterator.current(), "Size");
if (sizeProperty != nullptr)
{
std::string sizeValue = sizeProperty->findAttribute("value");
auto sizeValue = sizeProperty->findAttribute("value");
if (!sizeValue.empty())
return MyGUI::IntSize::parse(sizeValue);
}
@ -608,13 +608,13 @@ namespace Gui
MyGUI::ResourceManager::getInstance().addResource(bookFont);
}
void FontLoader::overrideLineHeight(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)
void FontLoader::overrideLineHeight(MyGUI::xml::ElementPtr _node, std::string_view _file, MyGUI::Version _version)
{
// We should adjust line height for MyGUI widgets depending on font size
MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator();
while (resourceNode.next("Resource"))
{
std::string type = resourceNode->findAttribute("type");
auto type = resourceNode->findAttribute("type");
if (Misc::StringUtils::ciEqual(type, "ResourceLayout"))
{

View File

@ -27,7 +27,7 @@ namespace Gui
public:
FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor);
void overrideLineHeight(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version);
void overrideLineHeight(MyGUI::xml::ElementPtr _node, std::string_view _file, MyGUI::Version _version);
static std::string_view getFontForFace(std::string_view face);

View File

@ -73,11 +73,7 @@ namespace LuaUi
w->eventMouseButtonPressed.clear();
w->eventMouseButtonReleased.clear();
w->eventMouseMove.clear();
#if MYGUI_VERSION <= MYGUI_DEFINE_VERSION(3, 4, 2)
w->eventMouseDrag.m_event.clear();
#else
w->eventMouseDrag.clear();
#endif
w->eventMouseSetFocus.clear();
w->eventMouseLostFocus.clear();
@ -92,15 +88,21 @@ namespace LuaUi
w->widget()->detachFromWidget();
}
void WidgetExtension::updateVisible()
{
// workaround for MyGUI bug
// parent visibility doesn't affect added children
MyGUI::Widget* widget = this->widget();
MyGUI::Widget* parent = widget->getParent();
bool inheritedVisible = widget->getVisible() && (parent == nullptr || parent->getInheritedVisible());
widget->setVisible(inheritedVisible);
}
void WidgetExtension::attach(WidgetExtension* ext)
{
ext->mParent = this;
ext->mTemplateChild = false;
ext->widget()->attachToWidget(mSlot->widget());
// workaround for MyGUI bug
// parent visibility doesn't affect added children
ext->widget()->setVisible(!ext->widget()->getVisible());
ext->widget()->setVisible(!ext->widget()->getVisible());
}
void WidgetExtension::attachTemplate(WidgetExtension* ext)
@ -108,10 +110,6 @@ namespace LuaUi
ext->mParent = this;
ext->mTemplateChild = true;
ext->widget()->attachToWidget(widget());
// workaround for MyGUI bug
// parent visibility doesn't affect added children
ext->widget()->setVisible(!ext->widget()->getVisible());
ext->widget()->setVisible(!ext->widget()->getVisible());
}
WidgetExtension* WidgetExtension::findDeep(std::string_view flagName)
@ -256,6 +254,8 @@ namespace LuaUi
void WidgetExtension::updateCoord()
{
updateVisible();
MyGUI::IntCoord oldCoord = mWidget->getCoord();
MyGUI::IntCoord newCoord = calculateCoord();

View File

@ -173,6 +173,8 @@ namespace LuaUi
void focusLoss(MyGUI::Widget*, MyGUI::Widget*);
std::optional<std::function<void(WidgetExtension*, MyGUI::IntCoord)>> mOnCoordChange;
void updateVisible();
};
class LuaWidget : public MyGUI::Widget, public WidgetExtension

View File

@ -16,11 +16,7 @@ namespace LuaUi
for (auto& [w, _] : mActionWidgets)
{
w->eventMouseButtonPressed.clear();
#if MYGUI_VERSION <= MYGUI_DEFINE_VERSION(3, 4, 2)
w->eventMouseDrag.m_event.clear();
#else
w->eventMouseDrag.clear();
#endif
}
mActionWidgets.clear();

View File

@ -58,22 +58,17 @@ namespace osgMyGUI
throw std::runtime_error("DataManager::getDataListNames is not implemented - VFS is used");
}
const std::string& DataManager::getDataPath(const std::string& name) const
std::string DataManager::getDataPath(const std::string& name) const
{
static std::string result;
result.clear();
if (name.empty())
{
result = Files::pathToUnicodeString(mResourcePath);
return result;
return Files::pathToUnicodeString(mResourcePath);
}
if (!isDataExist(name))
return result;
return {};
result = Files::pathToUnicodeString(mResourcePath / name);
return result;
return Files::pathToUnicodeString(mResourcePath / name);
}
}

View File

@ -45,7 +45,7 @@ namespace osgMyGUI
@param _name Resource name.
@return Return full path to specified data.
*/
const std::string& getDataPath(const std::string& _name) const override;
std::string getDataPath(const std::string& _name) const override;
private:
std::filesystem::path mResourcePath;

View File

@ -25,12 +25,12 @@ namespace osgMyGUI
mStream.flush();
}
void CustomLogListener::log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time,
const std::string& _message, const char* _file, int _line)
void CustomLogListener::log(std::string_view _section, MyGUI::LogLevel _level, const struct tm* _time,
std::string_view _message, std::string_view _file, int _line)
{
if (mStream.is_open())
{
const char* separator = " | ";
std::string_view separator = " | ";
mStream << std::setw(2) << std::setfill('0') << _time->tm_hour << ":" << std::setw(2) << std::setfill('0')
<< _time->tm_min << ":" << std::setw(2) << std::setfill('0') << _time->tm_sec << separator
<< _section << separator << _level.print() << separator << _message << separator << _file

View File

@ -30,8 +30,8 @@ namespace osgMyGUI
void close() override;
void flush() override;
void log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time,
const std::string& _message, const char* _file, int _line) override;
void log(std::string_view _section, MyGUI::LogLevel _level, const struct tm* _time, std::string_view _message,
std::string_view _file, int _line) override;
private:
std::ofstream mStream;

View File

@ -66,31 +66,19 @@ namespace osgMyGUI
void enableShaders(Shader::ShaderManager& shaderManager);
void setScalingFactor(float factor);
static RenderManager& getInstance() { return *getInstancePtr(); }
static RenderManager* getInstancePtr()
{
return static_cast<RenderManager*>(MyGUI::RenderManager::getInstancePtr());
}
bool checkTexture(MyGUI::ITexture* _texture)
#if MYGUI_DEBUG_MODE == 1 /* needed workaround for MyGUI 3.4.2 */
override
#endif
;
bool checkTexture(MyGUI::ITexture* _texture) override;
/** @see RenderManager::getViewSize */
const MyGUI::IntSize& getViewSize() const override
{
return mViewSize;
}
const MyGUI::IntSize& getViewSize() const override { return mViewSize; }
/** @see RenderManager::getVertexFormat */
MyGUI::VertexColourType getVertexFormat() const override
{
return mVertexFormat;
}
MyGUI::VertexColourType getVertexFormat() const override { return mVertexFormat; }
/** @see RenderManager::isFormatSupported */
bool isFormatSupported(MyGUI::PixelFormat format, MyGUI::TextureUsage usage) override;
@ -123,10 +111,7 @@ namespace osgMyGUI
void setInjectState(osg::StateSet* stateSet);
/** @see IRenderTarget::getInfo */
const MyGUI::RenderTargetInfo& getInfo() const override
{
return mInfo;
}
const MyGUI::RenderTargetInfo& getInfo() const override { return mInfo; }
void setViewSize(int width, int height) override;

View File

@ -123,8 +123,8 @@ namespace osgMyGUI
{
if (info->getName() == "Property")
{
const std::string& key = info->findAttribute("key");
const std::string& value = info->findAttribute("value");
auto key = info->findAttribute("key");
auto value = info->findAttribute("value");
if (key == "Size")
{

View File

@ -308,6 +308,7 @@ namespace Nif
// Modifiers, 4.0.0.2
{ "NiGravity", &construct<NiGravity, RC_NiGravity> },
{ "NiParticleBomb", &construct<NiParticleBomb, RC_NiParticleBomb> },
{ "NiParticleColorModifier", &construct<NiParticleColorModifier, RC_NiParticleColorModifier> },
{ "NiParticleGrowFade", &construct<NiParticleGrowFade, RC_NiParticleGrowFade> },
{ "NiParticleRotation", &construct<NiParticleRotation, RC_NiParticleRotation> },

View File

@ -51,6 +51,20 @@ namespace Nif
nif->read(mDirection);
}
void NiParticleBomb::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
nif->read(mRange);
nif->read(mDuration);
nif->read(mStrength);
nif->read(mStartTime);
mDecayType = static_cast<DecayType>(nif->get<uint32_t>());
mSymmetryType = static_cast<SymmetryType>(nif->get<uint32_t>());
nif->read(mPosition);
nif->read(mDirection);
}
void NiParticleCollider::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
@ -294,10 +308,10 @@ namespace Nif
mBombObject.read(nif);
nif->read(mBombAxis);
nif->read(mDecay);
nif->read(mDeltaV);
nif->read(mDecayType);
nif->read(mSymmetryType);
nif->read(mRange);
nif->read(mStrength);
mDecayType = static_cast<DecayType>(nif->get<uint32_t>());
mSymmetryType = static_cast<SymmetryType>(nif->get<uint32_t>());
}
void NiPSysBombModifier::post(Reader& nif)

View File

@ -40,6 +40,20 @@ namespace Nif
Point = 1, // Fixed origin
};
enum class DecayType : uint32_t
{
None = 0, // f(Distance) = 1.0
Linear = 1, // f(Distance) = (Range - Distance) / Range
Exponential = 2, // f(Distance) = exp(-Distance / Range)
};
enum class SymmetryType : uint32_t
{
Spherical = 0,
Cylindrical = 1, // Perpendicular to direction axis
Planar = 2, // Parallel to direction axis
};
struct NiGravity : NiParticleModifier
{
float mDecay{ 0.f };
@ -51,6 +65,20 @@ namespace Nif
void read(NIFStream* nif) override;
};
struct NiParticleBomb : NiParticleModifier
{
float mRange;
float mDuration;
float mStrength;
float mStartTime;
DecayType mDecayType;
SymmetryType mSymmetryType;
osg::Vec3f mPosition;
osg::Vec3f mDirection;
void read(NIFStream* nif);
};
struct NiParticleCollider : NiParticleModifier
{
float mBounceFactor;
@ -210,10 +238,10 @@ namespace Nif
{
NiAVObjectPtr mBombObject;
osg::Vec3f mBombAxis;
float mDecay;
float mDeltaV;
uint32_t mDecayType;
uint32_t mSymmetryType;
float mRange;
float mStrength;
DecayType mDecayType;
SymmetryType mSymmetryType;
void read(NIFStream* nif) override;
void post(Reader& nif) override;

View File

@ -206,6 +206,7 @@ namespace Nif
RC_NiMultiTargetTransformController,
RC_NiNode,
RC_NiPalette,
RC_NiParticleBomb,
RC_NiParticleColorModifier,
RC_NiParticleGrowFade,
RC_NiParticleRotation,

View File

@ -1092,6 +1092,18 @@ namespace NifOsg
const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(modifier.getPtr());
program->addOperator(new GravityAffector(gr));
}
else if (modifier->recType == Nif::RC_NiParticleBomb)
{
auto bomb = static_cast<const Nif::NiParticleBomb*>(modifier.getPtr());
osg::ref_ptr<osgParticle::ModularProgram> bombProgram(new osgParticle::ModularProgram);
attachTo->addChild(bombProgram);
bombProgram->setParticleSystem(partsys);
bombProgram->setReferenceFrame(rf);
bombProgram->setStartTime(bomb->mStartTime);
bombProgram->setLifeTime(bomb->mDuration);
bombProgram->setEndless(false);
bombProgram->addOperator(new ParticleBomb(bomb));
}
else if (modifier->recType == Nif::RC_NiParticleColorModifier)
{
const Nif::NiParticleColorModifier* cl

View File

@ -346,6 +346,84 @@ namespace NifOsg
}
}
ParticleBomb::ParticleBomb(const Nif::NiParticleBomb* bomb)
: mRange(bomb->mRange)
, mStrength(bomb->mStrength)
, mDecayType(bomb->mDecayType)
, mSymmetryType(bomb->mSymmetryType)
, mPosition(bomb->mPosition)
, mDirection(bomb->mDirection)
{
}
ParticleBomb::ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop)
: osgParticle::Operator(copy, copyop)
{
mRange = copy.mRange;
mStrength = copy.mStrength;
mDecayType = copy.mDecayType;
mSymmetryType = copy.mSymmetryType;
mCachedWorldPosition = copy.mCachedWorldPosition;
mCachedWorldDirection = copy.mCachedWorldDirection;
}
void ParticleBomb::beginOperate(osgParticle::Program* program)
{
bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);
mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition;
// We don't need the direction for Spherical bomb
if (mSymmetryType != Nif::SymmetryType::Spherical)
{
mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
mCachedWorldDirection.normalize();
}
}
void ParticleBomb::operate(osgParticle::Particle* particle, double dt)
{
float decay = 1.f;
osg::Vec3f explosionDir;
osg::Vec3f particleDir = particle->getPosition() - mCachedWorldPosition;
float distance = particleDir.length();
particleDir.normalize();
switch (mDecayType)
{
case Nif::DecayType::None:
break;
case Nif::DecayType::Linear:
decay = 1.f - distance / mRange;
break;
case Nif::DecayType::Exponential:
decay = std::exp(-distance / mRange);
break;
}
if (decay <= 0.f)
return;
switch (mSymmetryType)
{
case Nif::SymmetryType::Spherical:
explosionDir = particleDir;
break;
case Nif::SymmetryType::Cylindrical:
explosionDir = particleDir - mCachedWorldDirection * (mCachedWorldDirection * particleDir);
explosionDir.normalize();
break;
case Nif::SymmetryType::Planar:
explosionDir = mCachedWorldDirection;
if (explosionDir * particleDir < 0)
explosionDir = -explosionDir;
break;
}
particle->addVelocity(explosionDir * mStrength * decay * dt);
}
Emitter::Emitter()
: osgParticle::Emitter()
, mFlags(0)

View File

@ -199,6 +199,31 @@ namespace NifOsg
osg::Vec3f mCachedWorldDirection;
};
class ParticleBomb : public osgParticle::Operator
{
public:
ParticleBomb(const Nif::NiParticleBomb* bomb);
ParticleBomb() = default;
ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
ParticleBomb& operator=(const ParticleBomb&) = delete;
META_Object(NifOsg, ParticleBomb)
void operate(osgParticle::Particle* particle, double dt) override;
void beginOperate(osgParticle::Program*) override;
private:
float mRange{ 0.f };
float mStrength{ 0.f };
Nif::DecayType mDecayType{ Nif::DecayType::None };
Nif::SymmetryType mSymmetryType{ Nif::SymmetryType::Spherical };
osg::Vec3f mPosition;
osg::Vec3f mDirection;
osg::Vec3f mCachedWorldPosition;
osg::Vec3f mCachedWorldDirection;
};
// NodeVisitor to find a Group node with the given record index, stored in the node's user data container.
// Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node).
class FindGroupByRecIndex : public osg::NodeVisitor

View File

@ -1,5 +1,7 @@
#include "riggeometry.hpp"
#include <unordered_map>
#include <osg/MatrixTransform>
#include <osgUtil/CullVisitor>
@ -10,39 +12,10 @@
#include "skeleton.hpp"
#include "util.hpp"
namespace
{
inline void accumulateMatrix(
const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result)
{
osg::Matrixf m = invBindMatrix * matrix;
float* ptr = m.ptr();
float* ptrresult = result.ptr();
ptrresult[0] += ptr[0] * weight;
ptrresult[1] += ptr[1] * weight;
ptrresult[2] += ptr[2] * weight;
ptrresult[4] += ptr[4] * weight;
ptrresult[5] += ptr[5] * weight;
ptrresult[6] += ptr[6] * weight;
ptrresult[8] += ptr[8] * weight;
ptrresult[9] += ptr[9] * weight;
ptrresult[10] += ptr[10] * weight;
ptrresult[12] += ptr[12] * weight;
ptrresult[13] += ptr[13] * weight;
ptrresult[14] += ptr[14] * weight;
}
}
namespace SceneUtil
{
RigGeometry::RigGeometry()
: mSkeleton(nullptr)
, mLastFrameNumber(0)
, mBoundsFirstFrame(true)
{
setNumChildrenRequiringUpdateTraversal(1);
// update done in accept(NodeVisitor&)
@ -50,12 +23,7 @@ namespace SceneUtil
RigGeometry::RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop)
: Drawable(copy, copyop)
, mSkeleton(nullptr)
, mInfluenceMap(copy.mInfluenceMap)
, mBone2VertexVector(copy.mBone2VertexVector)
, mBoneSphereVector(copy.mBoneSphereVector)
, mLastFrameNumber(0)
, mBoundsFirstFrame(true)
, mData(copy.mData)
{
setSourceGeometry(copy.mSourceGeometry);
setNumChildrenRequiringUpdateTraversal(1);
@ -151,42 +119,18 @@ namespace SceneUtil
return false;
}
if (!mInfluenceMap)
if (!mData)
{
Log(Debug::Error) << "Error: No InfluenceMap set on RigGeometry";
Log(Debug::Error) << "Error: No influence data set on RigGeometry";
return false;
}
mBoneNodesVector.clear();
for (auto& bonePair : mBoneSphereVector->mData)
mNodes.clear();
for (const BoneInfo& info : mData->mBones)
{
const std::string& boneName = bonePair.first;
Bone* bone = mSkeleton->getBone(boneName);
if (!bone)
{
mBoneNodesVector.push_back(nullptr);
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
continue;
}
mBoneNodesVector.push_back(bone);
}
for (auto& pair : mBone2VertexVector->mData)
{
for (auto& weight : pair.first)
{
const std::string& boneName = weight.first.first;
Bone* bone = mSkeleton->getBone(boneName);
if (!bone)
{
mBoneNodesVector.push_back(nullptr);
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
continue;
}
mBoneNodesVector.push_back(bone);
}
mNodes.push_back(mSkeleton->getBone(info.mName));
if (!mNodes.back())
Log(Debug::Error) << "Error: RigGeometry did not find bone " << info.mName;
}
return true;
@ -226,25 +170,28 @@ namespace SceneUtil
osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(geom.getNormalArray());
osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(geom.getTexCoordArray(7));
int index = mBoneSphereVector->mData.size();
for (auto& pair : mBone2VertexVector->mData)
for (const auto& [influences, vertices] : mData->mInfluences)
{
osg::Matrixf resultMat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
for (auto& weight : pair.first)
for (const auto& [index, weight] : influences)
{
Bone* bone = mBoneNodesVector[index];
const Bone* bone = mNodes[index];
if (bone == nullptr)
continue;
accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat);
index++;
osg::Matrixf boneMat = mData->mBones[index].mInvBindMatrix * bone->mMatrixInSkeletonSpace;
float* boneMatPtr = boneMat.ptr();
float* resultMatPtr = resultMat.ptr();
for (int i = 0; i < 16; ++i, ++resultMatPtr, ++boneMatPtr)
if (i % 4 != 3)
*resultMatPtr += *boneMatPtr * weight;
}
if (mGeomToSkelMatrix)
resultMat *= (*mGeomToSkelMatrix);
for (auto& vertex : pair.second)
for (unsigned short vertex : vertices)
{
(*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]);
if (normalDst)
@ -291,15 +238,14 @@ namespace SceneUtil
osg::BoundingBox box;
int index = 0;
for (auto& boundPair : mBoneSphereVector->mData)
size_t index = 0;
for (const BoneInfo& info : mData->mBones)
{
Bone* bone = mBoneNodesVector[index];
const Bone* bone = mNodes[index++];
if (bone == nullptr)
continue;
index++;
osg::BoundingSpheref bs = boundPair.second;
osg::BoundingSpheref bs = info.mBoundSphere;
if (mGeomToSkelMatrix)
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
else
@ -357,35 +303,26 @@ namespace SceneUtil
void RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)
{
mInfluenceMap = influenceMap;
mData = new InfluenceData;
mData->mBones.reserve(influenceMap->mData.size());
typedef std::map<unsigned short, std::vector<BoneWeight>> Vertex2BoneMap;
Vertex2BoneMap vertex2BoneMap;
mBoneSphereVector = new BoneSphereVector;
mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());
mBone2VertexVector = new Bone2VertexVector;
for (auto& influencePair : mInfluenceMap->mData)
std::unordered_map<unsigned short, std::vector<BoneWeight>> vertexToInfluences;
size_t index = 0;
for (const auto& [boneName, bi] : influenceMap->mData)
{
const std::string& boneName = influencePair.first;
const BoneInfluence& bi = influencePair.second;
mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere);
mData->mBones.push_back({ boneName, bi.mBoundSphere, bi.mInvBindMatrix });
for (auto& weightPair : bi.mWeights)
{
std::vector<BoneWeight>& vec = vertex2BoneMap[weightPair.first];
vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second);
}
for (const auto& [vertex, weight] : bi.mWeights)
vertexToInfluences[vertex].emplace_back(index, weight);
index++;
}
Bone2VertexMap bone2VertexMap;
for (auto& vertexPair : vertex2BoneMap)
{
bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);
}
std::map<std::vector<BoneWeight>, VertexList> influencesToVertices;
for (const auto& [vertex, weights] : vertexToInfluences)
influencesToVertices[weights].emplace_back(vertex);
mBone2VertexVector->mData.reserve(bone2VertexMap.size());
mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end());
mData->mInfluences.reserve(influencesToVertices.size());
mData->mInfluences.assign(influencesToVertices.begin(), influencesToVertices.end());
}
void RigGeometry::accept(osg::NodeVisitor& nv)

View File

@ -36,6 +36,7 @@ namespace SceneUtil
// static parts of the model.
void compileGLObjects(osg::RenderInfo& renderInfo) const override {}
// TODO: Make InfluenceMap more similar to InfluenceData
struct BoneInfluence
{
osg::Matrixf mInvBindMatrix;
@ -84,35 +85,29 @@ namespace SceneUtil
osg::ref_ptr<osg::Geometry> mSourceGeometry;
osg::ref_ptr<const osg::Vec4Array> mSourceTangents;
Skeleton* mSkeleton;
Skeleton* mSkeleton{ nullptr };
osg::ref_ptr<osg::RefMatrix> mGeomToSkelMatrix;
osg::ref_ptr<InfluenceMap> mInfluenceMap;
typedef std::pair<std::string, osg::Matrixf> BoneBindMatrixPair;
typedef std::pair<BoneBindMatrixPair, float> BoneWeight;
typedef std::vector<unsigned short> VertexList;
typedef std::map<std::vector<BoneWeight>, VertexList> Bone2VertexMap;
struct Bone2VertexVector : public osg::Referenced
struct BoneInfo
{
std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mData;
std::string mName;
osg::BoundingSpheref mBoundSphere;
osg::Matrixf mInvBindMatrix;
};
osg::ref_ptr<Bone2VertexVector> mBone2VertexVector;
struct BoneSphereVector : public osg::Referenced
using BoneWeight = std::pair<size_t, float>;
using VertexList = std::vector<unsigned short>;
struct InfluenceData : public osg::Referenced
{
std::vector<std::pair<std::string, osg::BoundingSpheref>> mData;
std::vector<BoneInfo> mBones;
std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mInfluences;
};
osg::ref_ptr<BoneSphereVector> mBoneSphereVector;
std::vector<Bone*> mBoneNodesVector;
osg::ref_ptr<InfluenceData> mData;
std::vector<Bone*> mNodes;
unsigned int mLastFrameNumber;
bool mBoundsFirstFrame;
unsigned int mLastFrameNumber{ 0 };
bool mBoundsFirstFrame{ true };
bool initFromParentSkeleton(osg::NodeVisitor* nv);

View File

@ -167,7 +167,7 @@ namespace SceneUtil
"SceneUtil::DisableLight", "SceneUtil::MWShadowTechnique", "SceneUtil::TextKeyMapHolder",
"Shader::AddedState", "Shader::RemovedAlphaFunc", "NifOsg::FlipController",
"NifOsg::KeyframeController", "NifOsg::Emitter", "NifOsg::ParticleColorAffector",
"NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::GrowFadeAffector",
"NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::ParticleBomb", "NifOsg::GrowFadeAffector",
"NifOsg::InverseWorldMatrix", "NifOsg::StaticBoundingBoxCallback", "NifOsg::GeomMorpherController",
"NifOsg::UpdateMorphGeometry", "NifOsg::UVController", "NifOsg::VisController", "osgMyGUI::Drawable",
"osg::DrawCallback", "osg::UniformBufferObject", "osgOQ::ClearQueriesCallback",

View File

@ -40,7 +40,7 @@ namespace Gui
notifySizeChange(this);
}
void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value)
void AutoSizedTextBox::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (_key == "ExpandDirection")
{
@ -48,7 +48,7 @@ namespace Gui
}
else
{
Gui::TextBox::setPropertyOverride(_key, _value);
TextBox::setPropertyOverride(_key, _value);
}
}
@ -103,7 +103,7 @@ namespace Gui
setEditStatic(true);
}
void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value)
void AutoSizedEditBox::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (_key == "ExpandDirection")
{
@ -115,7 +115,7 @@ namespace Gui
}
else
{
Gui::EditBox::setPropertyOverride(_key, _value);
EditBox::setPropertyOverride(_key, _value);
}
}
@ -136,7 +136,7 @@ namespace Gui
notifySizeChange(this);
}
void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value)
void AutoSizedButton::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (_key == "ExpandDirection")
{
@ -144,9 +144,10 @@ namespace Gui
}
else
{
Gui::Button::setPropertyOverride(_key, _value);
Button::setPropertyOverride(_key, _value);
}
}
Box::Box()
: mSpacing(4)
, mPadding(0)
@ -159,7 +160,7 @@ namespace Gui
align();
}
bool Box::_setPropertyImpl(const std::string& _key, const std::string& _value)
bool Box::_setPropertyImpl(std::string_view _key, std::string_view _value)
{
if (_key == "Spacing")
mSpacing = MyGUI::utility::parseValue<int>(_value);
@ -260,7 +261,7 @@ namespace Gui
}
}
void HBox::setPropertyOverride(const std::string& _key, const std::string& _value)
void HBox::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (!Box::_setPropertyImpl(_key, _value))
MyGUI::Widget::setPropertyOverride(_key, _value);
@ -415,7 +416,7 @@ namespace Gui
}
}
void VBox::setPropertyOverride(const std::string& _key, const std::string& _value)
void VBox::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (!Box::_setPropertyImpl(_key, _value))
MyGUI::Widget::setPropertyOverride(_key, _value);

View File

@ -7,25 +7,8 @@
#include <MyGUI_TextBox.h>
#include <MyGUI_Widget.h>
#include "fontwrapper.hpp"
namespace Gui
{
class Button : public FontWrapper<MyGUI::Button>
{
MYGUI_RTTI_DERIVED(Button)
};
class TextBox : public FontWrapper<MyGUI::TextBox>
{
MYGUI_RTTI_DERIVED(TextBox)
};
class EditBox : public FontWrapper<MyGUI::EditBox>
{
MYGUI_RTTI_DERIVED(EditBox)
};
class AutoSizedWidget
{
public:
@ -44,7 +27,7 @@ namespace Gui
MyGUI::Align mExpandDirection;
};
class AutoSizedTextBox : public AutoSizedWidget, public TextBox
class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox
{
MYGUI_RTTI_DERIVED(AutoSizedTextBox)
@ -53,11 +36,11 @@ namespace Gui
void setCaption(const MyGUI::UString& _value) override;
protected:
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
std::string mFontSize;
};
class AutoSizedEditBox : public AutoSizedWidget, public EditBox
class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox
{
MYGUI_RTTI_DERIVED(AutoSizedEditBox)
@ -68,7 +51,7 @@ namespace Gui
void initialiseOverride() override;
protected:
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
int getWidth();
std::string mFontSize;
bool mShrink = false;
@ -76,7 +59,7 @@ namespace Gui
int mMaxWidth = 0;
};
class AutoSizedButton : public AutoSizedWidget, public Button
class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button
{
MYGUI_RTTI_DERIVED(AutoSizedButton)
@ -85,7 +68,7 @@ namespace Gui
void setCaption(const MyGUI::UString& _value) override;
protected:
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
std::string mFontSize;
};
@ -105,7 +88,7 @@ namespace Gui
protected:
virtual void align() = 0;
virtual bool _setPropertyImpl(const std::string& _key, const std::string& _value);
virtual bool _setPropertyImpl(std::string_view _key, std::string_view _value);
int mSpacing; // how much space to put between elements
@ -137,7 +120,7 @@ namespace Gui
void align() override;
MyGUI::IntSize getRequestedSize() override;
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
void onWidgetCreated(MyGUI::Widget* _widget) override;
};
@ -156,7 +139,7 @@ namespace Gui
void align() override;
MyGUI::IntSize getRequestedSize() override;
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
void onWidgetCreated(MyGUI::Widget* _widget) override;
};

View File

@ -1,40 +0,0 @@
#ifndef OPENMW_WIDGETS_WRAPPER_H
#define OPENMW_WIDGETS_WRAPPER_H
#include <MyGUI_Prerequest.h>
#include "components/settings/values.hpp"
#include <algorithm>
namespace Gui
{
template <class T>
class FontWrapper : public T
{
#if MYGUI_VERSION <= MYGUI_DEFINE_VERSION(3, 4, 2)
public:
void setFontName(const std::string& name) override
{
T::setFontName(name);
T::setPropertyOverride("FontHeight", std::to_string(Settings::gui().mFontSize));
}
protected:
void setPropertyOverride(const std::string& _key, const std::string& _value) override
{
T::setPropertyOverride(_key, _value);
// https://github.com/MyGUI/mygui/issues/113
// There is a bug in MyGUI: when it initializes the FontName property, it reset the font height.
// We should restore it.
if (_key == "FontName")
{
T::setPropertyOverride("FontHeight", std::to_string(Settings::gui().mFontSize));
}
}
#endif
};
}
#endif

View File

@ -34,7 +34,7 @@ namespace Gui
updateImage();
}
void ImageButton::setPropertyOverride(const std::string& _key, const std::string& _value)
void ImageButton::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (_key == "ImageHighlighted")
mImageHighlighted = _value;
@ -56,6 +56,7 @@ namespace Gui
else
ImageBox::setPropertyOverride(_key, _value);
}
void ImageButton::onMouseSetFocus(Widget* _old)
{
mMouseFocus = true;

View File

@ -32,7 +32,7 @@ namespace Gui
static bool sDefaultNeedKeyFocus;
protected:
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
void onMouseLostFocus(MyGUI::Widget* _new) override;
void onMouseSetFocus(MyGUI::Widget* _old) override;
void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override;

View File

@ -107,7 +107,7 @@ namespace Gui
mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition));
}
void MWList::setPropertyOverride(const std::string& _key, const std::string& _value)
void MWList::setPropertyOverride(std::string_view _key, std::string_view _value)
{
if (_key == "ListItemSkin")
mListItemSkin = _value;

View File

@ -49,7 +49,7 @@ namespace Gui
void scrollToTop();
void setPropertyOverride(const std::string& _key, const std::string& _value) override;
void setPropertyOverride(std::string_view _key, std::string_view _value) override;
protected:
void initialiseOverride() override;

View File

@ -3,15 +3,13 @@
#include <MyGUI_EditBox.h>
#include "fontwrapper.hpp"
namespace Gui
{
/**
* @brief A variant of the EditBox that only allows integer inputs
*/
class NumericEditBox final : public FontWrapper<MyGUI::EditBox>
class NumericEditBox final : public MyGUI::EditBox
{
MYGUI_RTTI_DERIVED(NumericEditBox)

View File

@ -3,8 +3,6 @@
#include <MyGUI_Button.h>
#include "fontwrapper.hpp"
namespace Gui
{
@ -14,7 +12,7 @@ namespace Gui
/// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a
/// ButtonGroup.
class SharedStateButton final : public FontWrapper<MyGUI::Button>
class SharedStateButton final : public MyGUI::Button
{
MYGUI_RTTI_DERIVED(SharedStateButton)

View File

@ -18,12 +18,9 @@ namespace Gui
MyGUI::FactoryManager::getInstance().registerFactory<Gui::HBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::Spacer>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::VBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::EditBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::TextBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedTextBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedEditBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::Button>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::ImageButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::NumericEditBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::SharedStateButton>("Widget");

View File

@ -23,9 +23,7 @@ force per pixel lighting
Force the use of per pixel lighting. By default, only bump mapped objects use per-pixel lighting.
Has no effect if the 'force shaders' option is false.
Enabling per-pixel lighting results in visual differences to the original MW engine.
It is not recommended to enable this option when using vanilla Morrowind files,
because certain lights in Morrowind rely on vertex lighting to look as intended.
Enabling per-pixel lighting results in visual differences to the original MW engine as certain lights in Morrowind rely on vertex lighting to look as intended.
Note that groundcover shaders ignore this setting.
clamp lighting

View File

@ -68,6 +68,7 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI)
set(MYGUI_BUILD_DEMOS OFF CACHE BOOL "")
set(MYGUI_BUILD_PLUGINS OFF CACHE BOOL "")
set(MYGUI_BUILD_TOOLS OFF CACHE BOOL "")
set(MYGUI_DONT_USE_OBSOLETE ON CACHE BOOL "")
if(MYGUI_STATIC)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
@ -77,8 +78,8 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI)
include(FetchContent)
FetchContent_Declare(mygui
URL https://github.com/MyGUI/mygui/archive/refs/tags/MyGUI3.4.2.zip
URL_HASH SHA512=d15de716102237ca55b952c2ab52f84b91766332a0357a50b17c20cf2f168666ddaab52d088d7bb8f713ad0fc27e19d74e6ae2673f310a8f60a3b5754f0a0ba7
URL https://github.com/MyGUI/mygui/archive/refs/tags/MyGUI3.4.3.zip
URL_HASH SHA512=c804ef665e786076582367f171082cd2181fdbd214300ddcca4a4245c5a0f45e62e72778ee2d96ec46b393e22477dd729f9bb3006e6eecbf536674e34a057721
SOURCE_DIR fetched/mygui
)
FetchContent_MakeAvailableExcludeFromAll(mygui)

View File

@ -226,12 +226,21 @@ return {
-- @function [parent=#UI] setPauseOnMode
-- @param #string mode Mode to configure
-- @param #boolean shouldPause
setPauseOnMode = function(mode, shouldPause) modePause[mode] = shouldPause end
setPauseOnMode = function(mode, shouldPause) modePause[mode] = shouldPause end,
--- Set whether the UI should be visible.
-- @function [parent=#UI] setHudVisibility
-- @param #boolean showHud
setHudVisibility = function(showHud) ui._setHudVisibility(showHud) end,
---
-- Returns if the player HUD is visible or not
-- @function [parent=#UI] isHudVisible
-- @return #bool
isHudVisible = function() return ui._isHudVisible() end,
-- TODO
-- registerHudElement = function(name, showFn, hideFn) end,
-- showHud = function(bool) end,
-- isHudVisible = function() end,
-- showHudElement = function(name, bool) end,
-- hudElements, -- map from element name to its visibility
},

View File

@ -174,6 +174,7 @@
-- @field #any type Type of the object (one of the tables from the package @{openmw.types#types}).
-- @field #number count Count (>1 means a stack of objects).
-- @field #string recordId Returns record ID of the object in lowercase.
-- @field #string globalVariable Global Variable associated with this object(read only).
---
-- Does the object still exist and is available.
@ -681,11 +682,13 @@
---
-- @type ActiveEffect
-- Magic effect that is currently active on an actor.
-- Note that when this effect expires or is removed, it will remain temporarily. Magnitude will be set to 0 for effects that expire.
-- @field #string affectedSkill Optional skill ID
-- @field #string affectedAttribute Optional attribute ID
-- @field #string id Effect id string
-- @field #string name Localized name of the effect
-- @field #number magnitude
-- @field #number magnitude current magnitude of the effect. Will be set to 0 when effect is removed or expires.
-- @field #number magnitudeBase
-- @field #number magnitudeModifier

View File

@ -29,6 +29,12 @@
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the script if there is a separate instance for each player. Currently has no effect.
-- @return #MWScript, #nil
---
-- Returns mutable global variables. In multiplayer, these may be specific to the provided player.
-- @function [parent=#MWScriptFunctions] getGlobalVariables
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the globals if there is a separate instance for each player. Currently has no effect.
-- @return #list<#number>
---
-- Returns global mwscript with given recordId. Returns `nil` if the script doesn't exist or is not started.
-- Currently there can be only one instance of each mwscript, but in multiplayer it will be possible to have a separate instance per player.

View File

@ -407,9 +407,8 @@ force shaders = false
# Force the use of per pixel lighting. By default, only bump mapped objects use per-pixel lighting.
# Has no effect if the 'force shaders' option is false.
# Enabling per-pixel lighting can result in visual differences to the original MW engine. It is not
# recommended to enable this option when using vanilla Morrowind files, because certain lights in Morrowind
# rely on vertex lighting to look as intended.
# Enabling per-pixel lighting can result in visual differences to the original MW engine as
# certain lights in Morrowind rely on vertex lighting to look as intended.
force per pixel lighting = false
# Restrict the amount of lighting that an object can receive to a maximum of (1,1,1).

View File

@ -19,7 +19,6 @@
void setupShadowCoords(vec4 viewPos, vec3 viewNormal)
{
#if SHADOWS
// This matrix has the opposite handedness to the others used here, so multiplication must have the vector to the left. Alternatively it could be transposed after construction, but that's extra work for the GPU just to make the code look a tiny bit cleaner.
vec4 shadowOffset;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
#if @perspectiveShadowMaps
@ -46,4 +45,4 @@ void setupShadowCoords(vec4 viewPos, vec3 viewNormal)
#endif
@endforeach
#endif // SHADOWS
}
}