From b1dfb0bc294b5b5580671961e6b6829d852a73b7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Jul 2018 12:57:09 +0200 Subject: [PATCH 01/19] stage1: priorities for event music and other minor improvements to the music system --- docs/openmw-stage1.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/openmw-stage1.md b/docs/openmw-stage1.md index 4707a4435..4290080b8 100644 --- a/docs/openmw-stage1.md +++ b/docs/openmw-stage1.md @@ -1383,9 +1383,9 @@ We add several new script instructions: * PlayBackgroundMusic (t) * PlaylistBackgroundMusic (pl) * StopBackgroundMusic -* PlayEventMusic (t, loop=0, force=0) -* PlaylistEventMusic (pl, loop=0, force=0) -* StopEventMusic +* PlayEventMusic (t, priority, loop=0, force=0) +* PlaylistEventMusic (pl, priority, loop=0, force=0) +* StopEventMusic (priority) Arguments: @@ -1393,8 +1393,14 @@ Arguments: * pl (string): a playlist (specified by its ID) * loop (integer): looping or not * force (integer): Terminate any other running event music first. If this flag is 0 and there is already event music playing the new event music is ignored. Event music provided by content developers will usually not set this flag. +* priority (integer): Priority for event music. If there are multiple instances of active event music only the one with the highest priority will play. There can only ever be one instance of event music for any priority level. If event music of higher priority stops and there is event music of lower priority the event music of lower priority starts to play. -Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. +Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. Changes in looping behaviour are considered though. + +New games starting with a new format omwgame file can use priorities as the developer sees fit. For old games where automatic injection of scripts and music records take place we define the following priorities: + +* 10: Combat music +* 0: Basic event music ### Transition of Existing System From a06c9c767d1beb2cc1222e3d2ed61a4c8f2cf4e4 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sun, 19 Aug 2018 18:36:43 +0300 Subject: [PATCH 02/19] Treat <> and << operators as < and >< and >> as > in scripts (bug #4597) --- CHANGELOG.md | 1 + components/compiler/scanner.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78e..4b573766a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button + Bug #4597: <> operator causes a compile error Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index bb0fb9374..f159b8b8b 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -502,6 +502,11 @@ namespace Compiler if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( putback (c); } + else if (c == '<' || c == '>') // Treat <> and << as < + { + special = S_cmpLT; + mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc); + } else { putback (c); @@ -525,6 +530,11 @@ namespace Compiler if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( putback (c); } + else if (c == '<' || c == '>') // Treat >< and >> as > + { + special = S_cmpGT; + mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc); + } else { putback (c); From e1afa1c2c38a5b18e4fd493b7b9682569aab7792 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Mon, 20 Aug 2018 12:49:26 -0500 Subject: [PATCH 03/19] Adding common problems that were previous on the site FAQ The [OpenMW FAQ](https://openmw.org/faq/) currently has two problems on it: a black screen and pink textures due to ST3C issues. Given that these are less common now, and that we have a dedicated space for "common problems" anyway, I figure that we should just move this to these questions to there. --- .../manuals/installation/common-problems.rst | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/source/manuals/installation/common-problems.rst b/docs/source/manuals/installation/common-problems.rst index 543b8ec9b..a44f2d581 100644 --- a/docs/source/manuals/installation/common-problems.rst +++ b/docs/source/manuals/installation/common-problems.rst @@ -47,4 +47,33 @@ Installing game files via Steam on macOS: DISK WRITE ERROR [other entries] } - Restart the Steam client. The download should now proceed. \ No newline at end of file + Restart the Steam client. The download should now proceed. + +In-game textures show up as pink +################################ + +:Symptoms: + Some textures don't show up and are replaced by pink "filler" textures. + +:Cause: + Textures shipped with Morrowind are compressed with S3TC, a texture compression algorithm that was patented in + the United States until October 2017. Software drivers and operating system distributions released before that date + may not include support for this algorithm. + +:Fix: + Upgrade your graphics drivers and/or operating system the latest version. + +Music plays, but only a black screen is shown +############################################# + +:Symptoms: + When launching OpenMW, audio is heard but there is only a black screen. + +:Cause: + This can occur when you did not import the Morrowind.ini file when the launcher asked for it. + +:Fix: + To fix it, you need to delete the `launcher.cfg` file from your configuration path + ([Path description on Wiki](https://wiki.openmw.org/index.php?title=Paths)), then start the launcher, select your + Morrowind installation, and when the launcher asks whether the `Morrowind.ini` file should be imported, make sure + to select "Yes". \ No newline at end of file From 9b10fe0edb0bc940eea8addecacc696b7f6b2869 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Tue, 21 Aug 2018 07:03:55 -0500 Subject: [PATCH 04/19] Addiong missing "to" word --- docs/source/manuals/installation/common-problems.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/manuals/installation/common-problems.rst b/docs/source/manuals/installation/common-problems.rst index a44f2d581..0967ea9ef 100644 --- a/docs/source/manuals/installation/common-problems.rst +++ b/docs/source/manuals/installation/common-problems.rst @@ -61,7 +61,7 @@ In-game textures show up as pink may not include support for this algorithm. :Fix: - Upgrade your graphics drivers and/or operating system the latest version. + Upgrade your graphics drivers and/or operating system to the latest version. Music plays, but only a black screen is shown ############################################# From b77d733c3eb50623e8f7ab0407b6feca1be16e3c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 21 Aug 2018 16:47:29 +0300 Subject: [PATCH 05/19] Fix freeze in getActorsSidingWith --- apps/openmw/mwmechanics/actors.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6f5b4eeb8..c28f6df28 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1790,14 +1790,13 @@ namespace MWMechanics // Actors that are targeted by this actor's Follow or Escort packages also side with them for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const MWWorld::Ptr &target = (*package)->getTarget(); - if ((*package)->sideWithTarget() && !target.isEmpty()) + if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) { if (iteratedActor == actor) { - list.push_back(target); + list.push_back((*package)->getTarget()); } - else if (target == actor) + else if ((*package)->getTarget() == actor) { list.push_back(iteratedActor); } From 910065f38fffce09f8db68d6598e262ad9b25c86 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 21 Aug 2018 17:02:56 +0300 Subject: [PATCH 06/19] Make some more optimizations to actor processing loops --- apps/openmw/mwmechanics/actors.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c28f6df28..d203dccf4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1782,6 +1782,8 @@ namespace MWMechanics if (iteratedActor == getPlayer()) continue; + const bool sameActor = (iteratedActor == actor); + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; @@ -1792,7 +1794,7 @@ namespace MWMechanics { if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) { - if (iteratedActor == actor) + if (sameActor) { list.push_back((*package)->getTarget()); } @@ -1815,7 +1817,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1878,7 +1880,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1908,8 +1910,11 @@ namespace MWMechanics getObjectsInRange(position, aiProcessingDistance, neighbors); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { + if (*neighbor == actor) + continue; + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); - if (stats.isDead() || *neighbor == actor) + if (stats.isDead()) continue; if (stats.getAiSequence().isInCombat(actor)) From 2b45fd84ead5ee55aa5e00e6bddf61d24ea4bacb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 19 Aug 2018 20:41:09 +0300 Subject: [PATCH 07/19] Play landing sound manually and ignore land soundgen textkeys (bug #2256) --- CHANGELOG.md | 1 + apps/openmw/mwclass/npc.cpp | 7 +++---- apps/openmw/mwmechanics/character.cpp | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78e..a01948943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Bug #1990: Sunrise/sunset not set correct Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards + Bug #2256: Landing sound not playing when jumping immediately after landing Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c06c3f67c..95d7fa66d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1241,11 +1241,10 @@ namespace MWClass { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) + if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return "DefaultLandWater"; - if(world->isOnGround(ptr)) - return "DefaultLand"; - return ""; + + return "DefaultLand"; } if(name == "swimleft") return "Swim Left"; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c7c6b57d0..be9daee88 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -939,10 +939,10 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); - if(!sound.empty()) + if(!sound.empty() && evt.compare(10, evt.size()-10, "land") != 0) // Don't play landing sounds here { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) + if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. @@ -2027,6 +2027,12 @@ void CharacterController::update(float duration) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } + + // Play landing sound + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); + if (!sound.empty()) + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } else { From 5a2e9868c1e65862f6c139da338de41f9e30aa98 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Tue, 21 Aug 2018 22:13:05 -0500 Subject: [PATCH 08/19] Fixes #3681 This changes the way that the popup works to use [`QtColorDialog::getColor()`](http://doc.qt.io/archives/qt-4.8/qcolordialog.html#getColor) instead of the problematic open() function. Also makes the button change to create the modal dialog when pushed, rather than being a checkbox of sorts --- apps/opencs/view/widget/coloreditor.cpp | 19 +------------------ apps/opencs/view/widget/coloreditor.hpp | 1 - apps/opencs/view/widget/colorpickerpopup.cpp | 13 +++---------- apps/opencs/view/widget/colorpickerpopup.hpp | 2 -- 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp index 5f82f8e26..82ca2b0c9 100644 --- a/apps/opencs/view/widget/coloreditor.cpp +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -1,11 +1,9 @@ #include "coloreditor.hpp" #include -#include #include #include #include -#include #include #include "colorpickerpopup.hpp" @@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart) mColorPicker(new ColorPickerPopup(this)), mPopupOnStart(popupOnStart) { - setCheckable(true); connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); - connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); } @@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt) void CSVWidget::ColorEditor::showPicker() { - if (isChecked()) - { - mColorPicker->showPicker(calculatePopupPosition(), mColor); - } - else - { - mColorPicker->hide(); - } -} - -void CSVWidget::ColorEditor::pickerHid() -{ - setChecked(false); - emit pickingFinished(); + mColorPicker->showPicker(calculatePopupPosition(), mColor); } void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp index 668f22cc9..368896e42 100644 --- a/apps/opencs/view/widget/coloreditor.hpp +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -45,7 +45,6 @@ namespace CSVWidget private slots: void showPicker(); - void pickerHid(); void pickerColorChanged(const QColor &color); signals: diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp index 8e71ce39e..47aab8981 100644 --- a/apps/opencs/view/widget/colorpickerpopup.cpp +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) mColorPicker->setWindowFlags(Qt::Widget); mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); mColorPicker->installEventFilter(this); - mColorPicker->open(); connect(mColorPicker, SIGNAL(currentColorChanged(const QColor &)), this, @@ -39,8 +37,9 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo geometry.moveTo(position); setGeometry(geometry); - mColorPicker->setCurrentColor(initialColor); - show(); + // Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel + QColor color = mColorPicker->getColor(initialColor); + mColorPicker->setCurrentColor(color); } void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) @@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) QFrame::mousePressEvent(event); } -void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) -{ - QFrame::hideEvent(event); - emit hid(); -} - bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) { if (object == mColorPicker && event->type() == QEvent::KeyPress) diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp index 602bbdb6d..eb5653f46 100644 --- a/apps/opencs/view/widget/colorpickerpopup.hpp +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -20,11 +20,9 @@ namespace CSVWidget protected: virtual void mousePressEvent(QMouseEvent *event); - virtual void hideEvent(QHideEvent *event); virtual bool eventFilter(QObject *object, QEvent *event); signals: - void hid(); void colorChanged(const QColor &color); }; } From ae0a6a22b3a0cdcfda40dec66e1963e90fdb1930 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 14:43:12 +0300 Subject: [PATCH 09/19] Move "land" check earlier --- apps/openmw/mwmechanics/character.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index be9daee88..686d0924a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -913,7 +913,8 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } - if(evt.compare(0, 10, "soundgen: ") == 0) + if(evt.compare(0, 10, "soundgen: ") == 0 + && evt.compare(10, evt.size()-10, "land") != 0) // Morrowind ignores land soundgen for some reason { std::string soundgen = evt.substr(10); @@ -939,7 +940,7 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); - if(!sound.empty() && evt.compare(10, evt.size()-10, "land") != 0) // Don't play landing sounds here + if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) From f74ebb64af6e9d64e6d180de9b49badc424762da Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 15:26:21 +0300 Subject: [PATCH 10/19] Correct special case soundgen comparisons --- apps/openmw/mwmechanics/character.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 686d0924a..49e2c4e6d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -913,8 +913,7 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } - if(evt.compare(0, 10, "soundgen: ") == 0 - && evt.compare(10, evt.size()-10, "land") != 0) // Morrowind ignores land soundgen for some reason + if(evt.compare(0, 10, "soundgen: ") == 0) { std::string soundgen = evt.substr(10); @@ -939,11 +938,14 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } + if (soundgen == "land") // Morrowind ignores land soundgen for some reason + return; + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) + if(soundgen == "left" || soundgen == "right") { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. From 3f76f1d3ed4d45e53fb72fdc923dd5389e79a69b Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 22:30:46 +0300 Subject: [PATCH 11/19] Fix gold count calculation in pickupObject (bug #4604) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b573766a..cf1bf91d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button Bug #4597: <> operator causes a compile error + Bug #4604: Picking up gold from the ground only makes 1 grabbed Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0b35bd9de..523409ceb 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -681,6 +681,8 @@ namespace MWGui return; int count = object.getRefData().getCount(); + if (object.getClass().isGold(object)) + count *= object.getClass().getValue(object); MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->breakInvisibility(player); From 3d4f5536d238d0957e8e53843af4a55eb76f3ee6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 24 Aug 2018 15:03:54 +0400 Subject: [PATCH 12/19] Check for impact immediately when launch a projectile (bug #3059) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwrender/weaponanimation.cpp | 9 ++++--- apps/openmw/mwworld/worldimp.cpp | 30 +++++++++++++++++++++--- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4c96cf9..1b8afe14b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue + Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c46298b0..5561d13ca 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -490,8 +490,8 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0; virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; - virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; + virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0; virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 0b3d000f3..054ce1b1d 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -123,7 +123,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength); showWeapon(false); @@ -149,9 +150,11 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWWorld::Ptr ammoPtr = *ammo; + MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength); - inv.remove(*ammo, 1, actor); + inv.remove(ammoPtr, 1, actor); mAmmunition.reset(); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c2ebac8fc..ae00595bb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2892,10 +2892,34 @@ namespace MWWorld } } - void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) + void World::launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) { - mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); + // An initial position of projectile can be outside shooter's collision box, so any object between shooter and launch position will be ignored. + // To avoid this issue, we should check for impact immediately before launch the projectile. + // So we cast a 1-yard-length ray from shooter to launch position and check if there are collisions in this area. + // TODO: as a better solutuon we should handle projectiles during physics update, not during world update. + const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0,-1,0) * 64.f; + + // Early out if the launch position is underwater + bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos); + if (underwater) + { + mRendering->emitWaterRipple(worldPos); + return; + } + + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer()) + actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); + + // Check for impact, if yes, handle hit, if not, launch projectile + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile); + if (result.mHit) + MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength); + else + mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); } void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 432991059..1c055cbf7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -607,8 +607,8 @@ namespace MWWorld void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override; void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override; - void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; + void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override; void applyLoopingParticles(const MWWorld::Ptr& ptr) override; From a560a9e00d93b1bdd00f4971a72b0645c969944c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 18 Aug 2018 16:12:01 +0400 Subject: [PATCH 13/19] Allow messageboxes arguments to have newline characters (bug #3836) --- CHANGELOG.md | 1 + components/compiler/scanner.cpp | 32 +++++++++++++++++++++++++++----- components/compiler/scanner.hpp | 6 ++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c016ca82f..945d114b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla + Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index bb0fb9374..257c3314e 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -306,10 +306,22 @@ namespace Compiler int i = 0; std::string lowerCase = Misc::StringUtils::lowerCase(name); - + bool isKeyword = false; for (; sKeywords[i]; ++i) if (lowerCase==sKeywords[i]) + { + isKeyword = true; break; + } + + // Russian localization and some mods use a quirk - add newline character directly + // to compiled bytecode via HEX-editor to implement multiline messageboxes. + // Of course, original editor will not compile such script. + // Allow messageboxes to bybass the "incomplete string or name" error. + if (lowerCase == "messagebox") + enableIgnoreNewlines(); + else if (isKeyword) + mIgnoreNewline = false; if (sKeywords[i]) { @@ -357,9 +369,14 @@ namespace Compiler // } else if (c=='\n') { - error = true; - mErrorHandler.error ("incomplete string or name", mLoc); - break; + if (mIgnoreNewline) + mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); + else + { + error = true; + mErrorHandler.error ("incomplete string or name", mLoc); + break; + } } } else if (!(c=='"' && name.empty())) @@ -578,7 +595,7 @@ namespace Compiler const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0), - mStrictKeywords (false), mTolerantNames (false) + mStrictKeywords (false), mTolerantNames (false), mIgnoreNewline(false) { } @@ -631,6 +648,11 @@ namespace Compiler mExtensions->listKeywords (keywords); } + void Scanner::enableIgnoreNewlines() + { + mIgnoreNewline = true; + } + void Scanner::enableStrictKeywords() { mStrictKeywords = true; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 49fbaa96a..a431cabb2 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -39,6 +39,7 @@ namespace Compiler TokenLoc mPutbackLoc; bool mStrictKeywords; bool mTolerantNames; + bool mIgnoreNewline; public: @@ -126,6 +127,11 @@ namespace Compiler void listKeywords (std::vector& keywords); ///< Append all known keywords to \a keywords. + /// Treat newline character as a part of script command. + /// + /// \attention This mode lasts only until the next keyword is reached. + void enableIgnoreNewlines(); + /// Do not accept keywords in quotation marks anymore. /// /// \attention This mode lasts only until the next newline is reached. From 67055b18c41a2599128f9e7bac1348467dd18a27 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Fri, 24 Aug 2018 12:51:18 -0500 Subject: [PATCH 14/19] Adding Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78e..b77c5be1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning + Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye From de08c1cb1b024761671a00a27621268d66c0fef6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 25 Aug 2018 10:34:33 +0400 Subject: [PATCH 15/19] Make Move and MoveWorld console commands move actors standing on moving object (bug #2274) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 1 + .../mwscript/transformationextensions.cpp | 35 +++++++++++++------ apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 1 + 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4c96cf9..0fe2edb0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards Bug #2256: Landing sound not playing when jumping immediately after landing + Bug #2274: Thin platform clips through player character instead of lifting Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c46298b0..115d2a217 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -412,6 +412,7 @@ namespace MWBase /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; + virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) = 0; ///< get a list of actors standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8f0c72519..16d7e8036 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -29,6 +29,18 @@ namespace MWScript { namespace Transformation { + void moveStandingActors(const MWWorld::Ptr &ptr, const osg::Vec3f& diff) + { + std::vector actors; + MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); + for (auto& actor : actors) + { + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + actorPos += diff; + MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z()); + } + } + template class OpSetScale : public Interpreter::Opcode0 { @@ -666,6 +678,10 @@ namespace MWScript osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange; osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3()); worldPos += diff; + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()); } }; @@ -688,22 +704,21 @@ namespace MWScript runtime.pop(); const float *objPos = ptr.getRefData().getPosition().pos; + osg::Vec3f diff; - MWWorld::Ptr updated; if (axis == "x") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); - } + diff.x() += movement; else if (axis == "y") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); - } + diff.y() += movement; else if (axis == "z") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); - } + diff.z() += movement; else throw std::runtime_error ("invalid movement axis: " + axis); + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c2ebac8fc..3ae5fd317 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2377,6 +2377,11 @@ namespace MWWorld return !actors.empty(); } + void World::getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) + { + mPhysics->getActorsStandingOn(object, actors); + } + bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 432991059..ca87ae5e3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -525,6 +525,7 @@ namespace MWWorld /// @note throws an exception when invoked on a teleport door void activateDoor(const MWWorld::Ptr& door, int state) override; + void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors); ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object From 4e3ae85c110591094745f7197cc445d913ec6dc3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 24 Aug 2018 01:54:50 -0700 Subject: [PATCH 16/19] Set the OpenAL source offset after setting the buffer This is to work around a bug in the Rapture3D driver. --- apps/openmw/mwsound/openal_output.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3569cd3da..076162fde 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1145,14 +1145,24 @@ bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset) initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); @@ -1175,14 +1185,24 @@ bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset) initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); From c2b3ca9638199a50f4d2f50cc3fdbdc26496088a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 24 Aug 2018 05:48:14 -0700 Subject: [PATCH 17/19] Update some comments It wasn't actually a bug in OSX like the comment said, but intended behavior. --- apps/openmw/mwsound/openal_output.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 076162fde..42dd6b5fd 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1217,9 +1217,8 @@ void OpenAL_Output::finishSound(Sound *sound) ALuint source = GET_PTRID(sound->mHandle); sound->mHandle = 0; - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); @@ -1322,9 +1321,8 @@ void OpenAL_Output::finishStream(Stream *sound) sound->mHandle = 0; mStreamThread->remove(stream); - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); From 77bdd0ea662d34f75bb4291e46de55b0e08a5b58 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 25 Aug 2018 01:24:19 -0700 Subject: [PATCH 18/19] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb77be8d..ef417facb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4606: Support Rapture3D's OpenAL driver 0.44.0 ------ From ff241fb7873ce412282eba10cc6c42a5b87b017e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 22 Aug 2018 12:48:05 +0400 Subject: [PATCH 19/19] Optimize skinning (task #4605) --- CHANGELOG.md | 1 + components/sceneutil/riggeometry.cpp | 77 ++++++++++++++-------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b573766a..b7f3272d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,7 @@ Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4605: Optimize skinning 0.44.0 ------ diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index ee30f1c85..c409bcd5c 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -8,6 +8,31 @@ #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 { @@ -141,28 +166,6 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) return true; } -void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, 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; -} - void RigGeometry::cull(osg::NodeVisitor* nv) { if (!mSkeleton) @@ -173,7 +176,8 @@ void RigGeometry::cull(osg::NodeVisitor* nv) return; } - if ((!mSkeleton->getActive() && mLastFrameNumber != 0) || mLastFrameNumber == nv->getTraversalNumber()) + unsigned int traversalNumber = nv->getTraversalNumber(); + if (mLastFrameNumber == traversalNumber || (mLastFrameNumber != 0 && !mSkeleton->getActive())) { osg::Geometry& geom = *getGeometry(mLastFrameNumber); nv->pushOntoNodePath(&geom); @@ -181,10 +185,10 @@ void RigGeometry::cull(osg::NodeVisitor* nv) nv->popFromNodePath(); return; } - mLastFrameNumber = nv->getTraversalNumber(); + mLastFrameNumber = traversalNumber; osg::Geometry& geom = *getGeometry(mLastFrameNumber); - mSkeleton->updateBoneMatrices(nv->getTraversalNumber()); + mSkeleton->updateBoneMatrices(traversalNumber); // skinning const osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); @@ -195,34 +199,31 @@ void RigGeometry::cull(osg::NodeVisitor* nv) osg::Vec3Array* normalDst = static_cast(geom.getNormalArray()); osg::Vec4Array* tangentDst = static_cast(geom.getTexCoordArray(7)); - for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it) + for (auto &pair : mBone2VertexMap) { - osg::Matrixf resultMat (0, 0, 0, 0, + osg::Matrixf resultMat (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - for (std::vector::const_iterator weightIt = it->first.begin(); weightIt != it->first.end(); ++weightIt) + for (auto &weight : pair.first) { - Bone* bone = weightIt->first.first; - const osg::Matrix& invBindMatrix = weightIt->first.second; - float weight = weightIt->second; - const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; - accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); + accumulateMatrix(weight.first.second, weight.first.first->mMatrixInSkeletonSpace, weight.second, resultMat); } + if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - for (std::vector::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt) + for (auto &vertex : pair.second) { - unsigned short vertex = *vertexIt; (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); if (normalDst) - (*normalDst)[vertex] = osg::Matrix::transform3x3((*normalSrc)[vertex], resultMat); + (*normalDst)[vertex] = osg::Matrixf::transform3x3((*normalSrc)[vertex], resultMat); + if (tangentDst) { - osg::Vec4f srcTangent = (*tangentSrc)[vertex]; - osg::Vec3f transformedTangent = osg::Matrix::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); + const osg::Vec4f& srcTangent = (*tangentSrc)[vertex]; + osg::Vec3f transformedTangent = osg::Matrixf::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); (*tangentDst)[vertex] = osg::Vec4f(transformedTangent, srcTangent.w()); } }