1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-04 03:40:14 +00:00

Merge branch 'master' into HEAD

This commit is contained in:
elsid 2019-08-27 20:49:12 +02:00
commit cb0a609d59
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40
55 changed files with 469 additions and 365 deletions

View File

@ -98,6 +98,7 @@
Bug #4984: "Friendly hits" feature should be used only for player's followers
Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent
Bug #4990: Dead bodies prevent you from hitting
Bug #4991: Jumping occasionally takes too much fatigue
Bug #4999: Drop instruction behaves differently from vanilla
Bug #5001: Possible data race in the Animation::setAlpha()
Bug #5004: Werewolves shield their eyes during storm
@ -115,11 +116,13 @@
Bug #5069: Blocking creatures' attacks doesn't degrade shields
Bug #5074: Paralyzed actors greet the player
Bug #5075: Enchanting cast style can be changed if there's no object
Bug #5078: DisablePlayerLooking is broken
Bug #5082: Scrolling with controller in GUI mode is broken
Bug #5089: Swimming/Underwater creatures only swim around ground level
Bug #5092: NPCs with enchanted weapons play sound when out of charges
Bug #5093: Hand to hand sound plays on knocked out enemies
Bug #5099: Non-swimming enemies will enter water if player is water walking
Bug #5103: Sneaking state behavior is still inconsistent
Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels
Bug #5105: NPCs start combat with werewolves from any distance
Bug #5106: Still can jump even when encumbered
@ -127,6 +130,9 @@
Bug #5112: Insufficient magicka for current spell not reflected on HUD icon
Bug #5123: Script won't run on respawn
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled
Bug #5126: Swimming creatures without RunForward animations are motionless during combat
Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls
@ -147,6 +153,7 @@
Feature #4812: Support NiSwitchNode
Feature #4836: Daytime node switch
Feature #4859: Make water reflections more configurable
Feature #4882: Support for NiPalette node
Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable
Feature #4958: Support eight blood types
@ -162,6 +169,7 @@
Feature #5036: Allow scripted faction leaving
Feature #5046: Gamepad thumbstick cursor speed
Feature #5051: Provide a separate textures for scrollbars
Feature #5091: Human-readable light source duration
Feature #5094: Unix like console hotkeys
Feature #5098: Allow user controller bindings
Feature #5121: Handle NiTriStrips and NiTriStripsData

View File

@ -9,6 +9,7 @@
#include <components/esm/cellid.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/doorstate.hpp"
#include "../mwrender/rendermode.hpp"
@ -407,7 +408,6 @@ namespace MWBase
virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable) = 0;
virtual void allowVanityMode(bool allow) = 0;
virtual void togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0;
virtual bool vanityRotateCamera(float * rot) = 0;
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
@ -420,7 +420,7 @@ namespace MWBase
/// update movement state of a non-teleport door as specified
/// @param state see MWClass::setDoorState
/// @note throws an exception when invoked on a teleport door
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
virtual void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0;
virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &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

View File

@ -103,7 +103,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View File

@ -105,7 +105,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -211,7 +211,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -121,7 +121,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -169,7 +169,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -271,7 +271,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text;
int lockLevel = ptr.getCellRef().getLockLevel();

View File

@ -586,7 +586,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View File

@ -34,7 +34,7 @@ namespace MWClass
class DoorCustomData : public MWWorld::CustomData
{
public:
int mDoorState; // 0 = nothing, 1 = opening, 2 = closing
MWWorld::DoorState mDoorState;
virtual MWWorld::CustomData *clone() const;
@ -71,7 +71,7 @@ namespace MWClass
if (ptr.getRefData().getCustomData())
{
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
if (customData.mDoorState > 0)
if (customData.mDoorState != MWWorld::DoorState::Idle)
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
}
@ -201,12 +201,12 @@ namespace MWClass
{
// animated door
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
int doorstate = getDoorState(ptr);
const auto doorState = getDoorState(ptr);
bool opening = true;
float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2];
if (doorstate == 1)
if (doorState == MWWorld::DoorState::Opening)
opening = false;
if (doorstate == 0 && doorRot != 0)
if (doorState == MWWorld::DoorState::Idle && doorRot != 0)
opening = false;
if (opening)
@ -293,7 +293,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text;
@ -365,20 +365,20 @@ namespace MWClass
{
std::unique_ptr<DoorCustomData> data(new DoorCustomData);
data->mDoorState = 0;
data->mDoorState = MWWorld::DoorState::Idle;
ptr.getRefData().setCustomData(data.release());
}
}
int Door::getDoorState (const MWWorld::ConstPtr &ptr) const
MWWorld::DoorState Door::getDoorState (const MWWorld::ConstPtr &ptr) const
{
if (!ptr.getRefData().getCustomData())
return 0;
return MWWorld::DoorState::Idle;
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
return customData.mDoorState;
}
void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const
void Door::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const
{
if (ptr.getCellRef().getTeleport())
throw std::runtime_error("load doors can't be moved");
@ -396,7 +396,7 @@ namespace MWClass
DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state);
customData.mDoorState = state2.mDoorState;
customData.mDoorState = static_cast<MWWorld::DoorState>(state2.mDoorState);
}
void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
@ -409,7 +409,7 @@ namespace MWClass
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
state2.mDoorState = customData.mDoorState;
state2.mDoorState = static_cast<int>(customData.mDoorState);
}
}

View File

@ -59,10 +59,9 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
/// 0 = nothing, 1 = opening, 2 = closing
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
virtual MWWorld::DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead.
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
virtual void setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

View File

@ -117,7 +117,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -151,18 +151,14 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;
if (Settings::Manager::getBool("show effect duration","Game"))
{
// -1 is infinite light source, so duration makes no sense here. Other negative values are treated as 0.
float remainingTime = ptr.getClass().getRemainingUsageTime(ptr);
if (remainingTime != -1.0f)
text += "\n#{sDuration}: " + MWGui::ToolTips::toString(std::max(0.f, remainingTime));
}
// Don't show duration for infinite light sources.
if (Settings::Manager::getBool("show effect duration","Game") && ptr.getClass().getRemainingUsageTime(ptr) != -1)
text += MWGui::ToolTips::getDurationString(ptr.getClass().getRemainingUsageTime(ptr), "\n#{sDuration}");
text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}");
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");

View File

@ -116,7 +116,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -159,7 +159,7 @@ namespace MWClass
else // gold displays its count also if it's 1.
countString = " (" + std::to_string(count) + ")";
info.caption = ref->mBase->mName + countString;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + countString;
info.icon = ref->mBase->mIcon;
if (ref->mRef.getSoul() != "")

View File

@ -940,10 +940,9 @@ namespace MWClass
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
bool swimming = world->isSwimming(ptr);
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
sneaking = sneaking && (inair || MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr));
bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
@ -1071,11 +1070,11 @@ namespace MWClass
bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp();
MWGui::ToolTipInfo info;
info.caption = getName(ptr);
info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())
{
info.caption += " (";
info.caption += ref->mBase->mName;
info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName);
info.caption += ")";
}

View File

@ -110,7 +110,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -116,7 +116,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -117,7 +117,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
std::string text;

View File

@ -168,7 +168,7 @@ namespace MWClass
const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();

View File

@ -137,27 +137,8 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") );
}
}
if (effectInfo.mRemainingTime > -1 &&
Settings::Manager::getBool("show effect duration","Game")) {
sourcesDescription += " #{sDuration}: ";
float duration = effectInfo.mRemainingTime;
if (duration > 3600)
{
int hour = duration / 3600;
duration -= hour*3600;
sourcesDescription += MWGui::ToolTips::toString(hour) + "h";
}
if (duration > 60)
{
int minute = duration / 60;
duration -= minute*60;
sourcesDescription += MWGui::ToolTips::toString(minute) + "m";
}
if (duration > 0.1)
{
sourcesDescription += MWGui::ToolTips::toString(duration) + "s";
}
}
if (effectInfo.mRemainingTime > -1 && Settings::Manager::getBool("show effect duration","Game"))
sourcesDescription += MWGui::ToolTips::getDurationString(effectInfo.mRemainingTime, " #{sDuration}");
addNewLine = true;
}

View File

@ -667,6 +667,60 @@ namespace MWGui
return ret;
}
std::string ToolTips::getDurationString(float duration, const std::string& prefix)
{
std::string ret;
ret = prefix + ": ";
if (duration < 1.f)
{
ret += "0 s";
return ret;
}
constexpr int secondsPerMinute = 60; // 60 seconds
constexpr int secondsPerHour = secondsPerMinute * 60; // 60 minutes
constexpr int secondsPerDay = secondsPerHour * 24; // 24 hours
constexpr int secondsPerMonth = secondsPerDay * 30; // 30 days
constexpr int secondsPerYear = secondsPerDay * 365;
int fullDuration = static_cast<int>(duration);
int units = 0;
int years = fullDuration / secondsPerYear;
int months = fullDuration % secondsPerYear / secondsPerMonth;
int days = fullDuration % secondsPerYear % secondsPerMonth / secondsPerDay; // Because a year is not exactly 12 "months"
int hours = fullDuration % secondsPerDay / secondsPerHour;
int minutes = fullDuration % secondsPerHour / secondsPerMinute;
int seconds = fullDuration % secondsPerMinute;
if (years)
{
units++;
ret += toString(years) + " y ";
}
if (months)
{
units++;
ret += toString(months) + " mo ";
}
if (units < 2 && days)
{
units++;
ret += toString(days) + " d ";
}
if (units < 2 && hours)
{
units++;
ret += toString(hours) + " h ";
}
if (units >= 2)
return ret;
if (minutes)
ret += toString(minutes) + " min ";
if (seconds)
ret += toString(seconds) + " s ";
return ret;
}
bool ToolTips::toggleFullHelp()
{
mFullHelp = !mFullHelp;

View File

@ -84,6 +84,9 @@ namespace MWGui
static std::string getCellRefString(const MWWorld::CellRef& cellref);
///< Returns a string containing debug tooltip information about the given cellref.
static std::string getDurationString (float duration, const std::string& prefix);
///< Returns duration as two largest time units, rounded down. Note: not localized; no line break.
// these do not create an actual tooltip, but they fill in the data that is required so the tooltip
// system knows what to show in case this widget is hovered
static void createSkillToolTip(MyGUI::Widget* widget, int skillId);

View File

@ -202,6 +202,11 @@ namespace MWInput
void InputManager::handleGuiArrowKey(int action)
{
// This is currently keyboard-specific code
// TODO: see if GUI controls can be refactored into a single function
if (mJoystickLastUsed)
return;
if (SDL_IsTextInputActive())
return;
@ -229,13 +234,10 @@ namespace MWInput
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);
}
bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release=false)
bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg)
{
// Presumption of GUI mode will be removed in the future.
// MyGUI KeyCodes *may* change.
// Currently button releases are ignored.
if (release)
return false;
MyGUI::KeyCode key = MyGUI::KeyCode::None;
switch (arg.button)
@ -386,9 +388,6 @@ namespace MWInput
case A_GameMenu:
toggleMainMenu ();
break;
case A_OptionsMenu:
toggleOptionsMenu();
break;
case A_Screenshot:
screenshot();
break;
@ -406,8 +405,7 @@ namespace MWInput
case A_MoveRight:
case A_MoveForward:
case A_MoveBackward:
// Temporary shut-down of this function until deemed necessary.
//handleGuiArrowKey(action);
handleGuiArrowKey(action);
break;
case A_Journal:
toggleJournal ();
@ -594,7 +592,7 @@ namespace MWInput
rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1);
// Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"])
{
mPlayer->yaw(rot[2]);
mPlayer->pitch(rot[0]);
@ -832,9 +830,6 @@ namespace MWInput
void InputManager::toggleControlSwitch (const std::string& sw, bool value)
{
if (mControlSwitch[sw] == value) {
return;
}
/// \note 7 switches at all, if-else is relevant
if (sw == "playercontrols" && !value) {
mPlayer->setLeftRight(0);
@ -846,8 +841,8 @@ namespace MWInput
mPlayer->setUpDown(0);
} else if (sw == "vanitymode") {
MWBase::Environment::get().getWorld()->allowVanityMode(value);
} else if (sw == "playerlooking") {
MWBase::Environment::get().getWorld()->togglePlayerLooking(value);
} else if (sw == "playerlooking" && !value) {
MWBase::Environment::get().getWorld()->rotateObject(mPlayer->getPlayer(), 0.f, 0.f, 0.f);
}
mControlSwitch[sw] = value;
}
@ -981,7 +976,7 @@ namespace MWInput
rot[2] = -x;
// Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"])
{
mPlayer->yaw(x);
mPlayer->pitch(y);
@ -1005,9 +1000,9 @@ namespace MWInput
mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (gamepadToGuiControl(arg, false))
if (gamepadToGuiControl(arg))
return;
else if (mGamepadGuiCursorEnabled)
if (mGamepadGuiCursorEnabled)
{
// Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
@ -1048,9 +1043,7 @@ namespace MWInput
mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (gamepadToGuiControl(arg, true))
return;
else if (mGamepadGuiCursorEnabled)
if (mGamepadGuiCursorEnabled)
{
// Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
@ -1149,37 +1142,19 @@ namespace MWInput
}
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
return;
bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame;
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings)
MWBase::Environment::get().getWindowManager()->popGuiMode();
if (inGame && mode != MWGui::GM_MainMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
}
void InputManager::toggleOptionsMenu()
{
if (MyGUI::InputManager::getInstance().isModalAny())
{
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
MWBase::Environment::get().getWindowManager()->toggleConsole();
return;
}
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
return;
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame;
if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings)
MWBase::Environment::get().getWindowManager()->popGuiMode();
if (inGame && mode != MWGui::GM_Settings)
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Settings);
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
}
else //Close current GUI
{
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
}
void InputManager::quickLoad() {
@ -1534,7 +1509,6 @@ namespace MWInput
defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B;
defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START;
defaultButtonBindings[A_OptionsMenu] = SDL_CONTROLLER_BUTTON_BACK;
defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE;
defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP;
defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
@ -1615,7 +1589,6 @@ namespace MWInput
descriptions[A_Journal] = "sJournal";
descriptions[A_Rest] = "sRestKey";
descriptions[A_Inventory] = "sInventory";
descriptions[A_OptionsMenu] = "sPreferences";
descriptions[A_TogglePOV] = "sTogglePOVCmd";
descriptions[A_QuickKeysMenu] = "sQuickMenu";
descriptions[A_QuickKey1] = "sQuick1Cmd";
@ -1753,7 +1726,6 @@ namespace MWInput
ret.push_back(A_Inventory);
ret.push_back(A_Journal);
ret.push_back(A_Rest);
ret.push_back(A_OptionsMenu);
ret.push_back(A_Console);
ret.push_back(A_QuickSave);
ret.push_back(A_QuickLoad);
@ -1786,7 +1758,6 @@ namespace MWInput
ret.push_back(A_Inventory);
ret.push_back(A_Journal);
ret.push_back(A_Rest);
ret.push_back(A_OptionsMenu);
ret.push_back(A_QuickSave);
ret.push_back(A_QuickLoad);
ret.push_back(A_Screenshot);

View File

@ -226,7 +226,7 @@ namespace MWInput
void setPlayerControlsEnabled(bool enabled);
void handleGuiArrowKey(int action);
// Return true if GUI consumes input.
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release);
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg);
bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);
void updateCursorMode();
@ -235,7 +235,6 @@ namespace MWInput
private:
void toggleMainMenu();
void toggleOptionsMenu();
void toggleSpell();
void toggleWeapon();
void toggleInventory();
@ -328,8 +327,6 @@ namespace MWInput
A_MoveForwardBackward,
A_MoveLeftRight,
A_OptionsMenu,
A_Last // Marker for the last item
};
};

View File

@ -1785,14 +1785,7 @@ namespace MWMechanics
MWWorld::Ptr player = getPlayer();
CreatureStats& stats = player.getClass().getCreatureStats(player);
MWBase::World* world = MWBase::Environment::get().getWorld();
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool inair = !world->isOnGround(player) && !world->isSwimming(player) && !world->isFlying(player);
sneaking = sneaking && (ctrl->isSneaking() || inair);
if (!sneaking)
if (!MWBase::Environment::get().getMechanicsManager()->isSneaking(player))
{
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
return;
@ -1800,6 +1793,7 @@ namespace MWMechanics
static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice"
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat();
static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat();

View File

@ -44,7 +44,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return true; // We have tried backing up for more than one second, we've probably cleared it
}
if (!mDoorPtr.getClass().getDoorState(mDoorPtr))
if (mDoorPtr.getClass().getDoorState(mDoorPtr) == MWWorld::DoorState::Idle)
return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door

View File

@ -232,7 +232,7 @@ void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
return;
// note: AiWander currently does not open doors
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0)
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == MWWorld::DoorState::Idle)
{
if ((door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 ))
{

View File

@ -529,19 +529,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
if(!mAnimation->hasAnimation(movementAnimName))
{
std::string::size_type swimpos = movementAnimName.find("swim");
if(swimpos == std::string::npos)
{
std::string::size_type runpos = movementAnimName.find("run");
if (runpos != std::string::npos)
{
movementAnimName.replace(runpos, runpos+3, "walk");
if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear();
}
else
movementAnimName.clear();
}
else
if (swimpos != std::string::npos)
{
movementAnimName.erase(swimpos, 4);
if (!weapShortGroup.empty())
@ -552,10 +540,20 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
else
movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
}
}
if (swimpos == std::string::npos || !mAnimation->hasAnimation(movementAnimName))
{
std::string::size_type runpos = movementAnimName.find("run");
if (runpos != std::string::npos)
{
movementAnimName.replace(runpos, runpos+3, "walk");
if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear();
}
else
movementAnimName.clear();
}
}
}
@ -570,10 +568,6 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
mCurrentMovement = movementAnimName;
if(!mCurrentMovement.empty())
{
bool isflying = MWBase::Environment::get().getWorld()->isFlying(mPtr);
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !isflying;
bool issneaking = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !isflying;
// For non-flying creatures, MW uses the Walk animation to calculate the animation velocity
// even if we are running. This must be replicated, otherwise the observed speed would differ drastically.
std::string anim = mCurrentMovement;
@ -606,7 +600,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
// The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded.
mMovementAnimSpeed = (issneaking ? 33.5452f : (isrunning ? 222.857f : 154.064f));
mMovementAnimSpeed = (isSneaking() ? 33.5452f : (isRunning() ? 222.857f : 154.064f));
mMovementAnimationControlled = false;
}
}
@ -2048,28 +2042,6 @@ void CharacterController::update(float duration, bool animationOnly)
lat.normalize();
vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f;
}
// advance acrobatics
// also set jumping flag to allow GetPCJumping works
if (isPlayer)
{
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
}
// decrease fatigue
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
const float fatigueDecrease = fatigueJumpBase + normalizedEncumbrance * fatigueJumpMult;
if (!godmode)
{
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
cls.getCreatureStats(mPtr).setFatigue(fatigue);
}
}
}
else if(mJumpState == JumpState_InAir && !inwater && !flying && solid)
@ -2282,7 +2254,9 @@ void CharacterController::update(float duration, bool animationOnly)
movement = vec;
cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0;
// Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame
if (movement.z() == 0.f)
cls.getMovementSettings(mPtr).mPosition[2] = 0;
// Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame
// due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled.
if (!mSkipAnim)

View File

@ -477,7 +477,12 @@ namespace MWMechanics
bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr)
{
return mActors.isSneaking(ptr);
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
MWBase::World* world = MWBase::Environment::get().getWorld();
bool animActive = mActors.isSneaking(ptr);
bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);
return stanceOn && (animActive || inair);
}
void MechanicsManager::rest(double hours, bool sleep)
@ -965,8 +970,7 @@ namespace MWMechanics
return true;
// check if a player tries to pickpocket a target NPC
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)
|| target.getClass().getCreatureStats(target).getKnockedDown())
if (target.getClass().getCreatureStats(target).getKnockedDown() || isSneaking(ptr))
return false;
return true;
@ -1586,9 +1590,7 @@ namespace MWMechanics
return false;
float sneakTerm = 0;
if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak)
&& !MWBase::Environment::get().getWorld()->isSwimming(ptr)
&& MWBase::Environment::get().getWorld()->isOnGround(ptr))
if (isSneaking(ptr))
{
static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
@ -1596,7 +1598,7 @@ namespace MWMechanics
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0;
if (ptr.getClass().isNpc())
if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{
const MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
MWWorld::ConstContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);

View File

@ -2,8 +2,6 @@
#include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
@ -54,10 +52,10 @@ namespace MWMechanics
// FIXME: cast
const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell());
int doorState = doorPtr.getClass().getDoorState(doorPtr);
const auto doorState = doorPtr.getClass().getDoorState(doorPtr);
float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2];
if (doorState != 0 || doorRot != 0)
if (doorState != MWWorld::DoorState::Idle || doorRot != 0)
continue; // the door is already opened/opening
doorPos.z() = 0;
@ -78,10 +76,8 @@ namespace MWMechanics
return MWWorld::Ptr(); // none found
}
ObstacleCheck::ObstacleCheck():
mPrevX(0) // to see if the moved since last time
, mPrevY(0)
, mWalkState(State_Norm)
ObstacleCheck::ObstacleCheck()
: mWalkState(State_Norm)
, mStuckDuration(0)
, mEvadeDuration(0)
, mDistSameSpot(-1) // avoid calculating it each time
@ -125,21 +121,15 @@ namespace MWMechanics
*/
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration)
{
const ESM::Position pos = actor.getRefData().getPosition();
const osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
if (mDistSameSpot == -1)
{
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y());
}
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
const float distSameSpot = mDistSameSpot * duration;
const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2();
const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot;
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
// update position
mPrevX = pos.pos[0];
mPrevY = pos.pos[1];
mPrev = pos;
switch(mWalkState)
{

View File

@ -1,6 +1,8 @@
#ifndef OPENMW_MECHANICS_OBSTACLE_H
#define OPENMW_MECHANICS_OBSTACLE_H
#include <osg/Vec3f>
namespace MWWorld
{
class Ptr;
@ -37,9 +39,8 @@ namespace MWMechanics
private:
// for checking if we're stuck (ignoring Z axis)
float mPrevX;
float mPrevY;
// for checking if we're stuck
osg::Vec3f mPrev;
// directions to try moving in when get stuck
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];

View File

@ -238,7 +238,7 @@ namespace MWMechanics
{
/* short group */ "",
/* long group */ "",
/* sound ID */ "Item Weapon Ammo",
/* sound ID */ "Item Ammo",
/* attach bone */ "ArrowBone",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman,
@ -252,7 +252,7 @@ namespace MWMechanics
{
/* short group */ "",
/* long group */ "",
/* sound ID */ "Item Weapon Ammo",
/* sound ID */ "Item Ammo",
/* attach bone */ "ArrowBone",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman,

View File

@ -34,6 +34,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/player.hpp"
#include "../mwrender/bulletdebugdraw.hpp"
@ -326,7 +327,30 @@ namespace MWPhysics
if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel)
velocity = osg::Vec3f(0,0,1) * 25;
if (ptr.getClass().getMovementSettings(ptr).mPosition[2])
{
const bool isPlayer = (ptr == MWMechanics::getPlayer());
// Advance acrobatics and set flag for GetPCJumping
if (isPlayer)
{
ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0);
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
}
// Decrease fatigue
if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState())
{
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr));
const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult;
MWMechanics::DynamicStat<float> fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue);
}
ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0;
}
// Now that we have the effective movement vector, apply wind forces to it
if (MWBase::Environment::get().getWorld()->isInStorm())

View File

@ -64,7 +64,7 @@ ActorAnimation::~ActorAnimation()
mScabbard.reset();
}
PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
{
osg::Group* parent = getBoneByName(bonename);
if (!parent)
@ -160,7 +160,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
if (showHolsteredWeapons)
{
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor);
mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
if (mScabbard)
resetControllers(mScabbard->getNode());
}
@ -168,7 +168,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
return;
}
mScabbard = getWeaponPart(scabbardName, boneName);
mScabbard = attachMesh(scabbardName, boneName);
osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
if (!weaponNode)

View File

@ -44,11 +44,11 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
virtual void updateQuiver();
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename)
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)
{
osg::Vec4f stubColor = osg::Vec4f(0,0,0,0);
return getWeaponPart(model, bonename, false, &stubColor);
return attachMesh(model, bonename, false, &stubColor);
};
PartHolderPtr mScabbard;

View File

@ -18,12 +18,14 @@
#include <components/resource/keyframemanager.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/nifosg/nifloader.hpp> // KeyframeHolder
#include <components/nifosg/controller.hpp>
#include <components/vfs/manager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/lightmanager.hpp>
@ -245,7 +247,10 @@ namespace
void apply(osg::Node& node)
{
if (SceneUtil::hasUserDescription(&node, "CustomBone"))
{
mFoundBones.emplace_back(&node, node.getParent(0));
return;
}
traverse(node);
}
@ -1378,6 +1383,9 @@ namespace MWRender
void injectCustomBones(osg::ref_ptr<osg::Node>& node, const std::string& model, Resource::ResourceSystem* resourceSystem)
{
if (model.empty())
return;
const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex();
std::string animationPath = model;
@ -1405,14 +1413,7 @@ namespace MWRender
}
}
enum InjectType
{
None,
Model,
ModelWithFallback
};
osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, InjectType inject)
osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, bool inject, const std::string& defaultSkeleton)
{
Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager();
if (baseonly)
@ -1424,11 +1425,11 @@ namespace MWRender
{
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem);
if (inject != InjectType::None)
if (inject)
{
injectCustomBones(created, defaultSkeleton, resourceSystem);
injectCustomBones(created, model, resourceSystem);
}
SceneUtil::CleanObjectRootVisitor removeDrawableVisitor;
created->accept(removeDrawableVisitor);
@ -1445,11 +1446,11 @@ namespace MWRender
{
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem);
if (inject != InjectType::None)
if (inject)
{
injectCustomBones(created, defaultSkeleton, resourceSystem);
injectCustomBones(created, model, resourceSystem);
}
return created;
}
@ -1475,17 +1476,44 @@ namespace MWRender
mAccumCtrl = nullptr;
static const bool useAdditionalSources = Settings::Manager::getBool ("use additional anim sources", "Game");
InjectType inject = useAdditionalSources && mPtr.getClass().isActor() ? InjectType::Model : InjectType::None;
if (inject != InjectType::None && isCreature)
std::string defaultSkeleton;
bool inject = false;
if (useAdditionalSources && mPtr.getClass().isActor())
{
if (isCreature)
{
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
if(ref->mBase->mFlags & ESM::Creature::Bipedal)
inject = InjectType::ModelWithFallback;
{
defaultSkeleton = "meshes\\xbase_anim.nif";
inject = true;
}
}
else
{
inject = true;
MWWorld::LiveCellRef<ESM::NPC> *ref = mPtr.get<ESM::NPC>();
if (!ref->mBase->mModel.empty())
{
// If NPC has a custom animation model attached, we should inject bones from default skeleton for given race and gender as well
// Since it is a quite rare case, there should not be a noticable performance loss
// Note: consider that player and werewolves have no custom animation files attached for now
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(ref->mBase->mRace);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
bool isFemale = !ref->mBase->isMale();
defaultSkeleton = SceneUtil::getActorSkeleton(false, isFemale, isBeast, false);
defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());
}
}
}
if (!forceskeleton)
{
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject);
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);
mInsert->addChild(created);
mObjectRoot = created->asGroup();
if (!mObjectRoot)
@ -1501,7 +1529,7 @@ namespace MWRender
}
else
{
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject);
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);
osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get());
if (!skel)
{

View File

@ -48,7 +48,6 @@ namespace MWRender
mAnimation(nullptr),
mFirstPersonView(true),
mPreviewMode(false),
mFreeLook(true),
mNearest(30.f),
mFurthest(800.f),
mIsNearest(false),
@ -393,11 +392,6 @@ namespace MWRender
camera = focal + offset;
}
void Camera::togglePlayerLooking(bool enable)
{
mFreeLook = enable;
}
bool Camera::isVanityOrPreviewModeEnabled()
{
return mPreviewMode || mVanity.enabled;

View File

@ -37,7 +37,6 @@ namespace MWRender
bool mFirstPersonView;
bool mPreviewMode;
bool mFreeLook;
float mNearest;
float mFurthest;
bool mIsNearest;
@ -119,8 +118,6 @@ namespace MWRender
/// Stores focal and camera world positions in passed arguments
void getPosition(osg::Vec3f &focal, osg::Vec3f &camera);
void togglePlayerLooking(bool enable);
bool isVanityOrPreviewModeEnabled();
bool isNearest();

View File

@ -1353,11 +1353,6 @@ namespace MWRender
mCamera->allowVanityMode(allow);
}
void RenderingManager::togglePlayerLooking(bool enable)
{
mCamera->togglePlayerLooking(enable);
}
void RenderingManager::changeVanityModeScale(float factor)
{
if(mCamera->isVanityOrPreviewModeEnabled())

View File

@ -208,7 +208,6 @@ namespace MWRender
void togglePreviewMode(bool enable);
bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow);
void togglePlayerLooking(bool enable);
void changeVanityModeScale(float factor);
/// temporarily override the field of view with given value.

View File

@ -186,14 +186,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
MWBase::World* world = MWBase::Environment::get().getWorld();
bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);
runtime.push(stanceOn && (sneaking || inair));
runtime.push(MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr));
}
};

View File

@ -188,13 +188,7 @@ namespace MWScript
// This is done when using Lock in scripts, but not when using Lock spells.
if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport())
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, 0);
float xr = ptr.getCellRef().getPosition().rot[0];
float yr = ptr.getCellRef().getPosition().rot[1];
float zr = ptr.getCellRef().getPosition().rot[2];
MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr);
MWBase::Environment::get().getWorld()->activateDoor(ptr, MWWorld::DoorState::Idle);
}
}
};

View File

@ -798,7 +798,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
if(alGetError() == AL_NO_ERROR)
Log(Debug::Info) << "Standard Reverb supported";
}
EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_GENERIC;
EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_LIVINGROOM;
props.flGain = 0.0f;
LoadEffect(mDefaultEffect, props);
}

View File

@ -460,12 +460,12 @@ namespace MWWorld
return false;
}
int Class::getDoorState (const MWWorld::ConstPtr &ptr) const
MWWorld::DoorState Class::getDoorState (const MWWorld::ConstPtr &ptr) const
{
throw std::runtime_error("this is not a door");
}
void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const
void Class::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const
{
throw std::runtime_error("this is not a door");
}

View File

@ -9,6 +9,7 @@
#include <osg/Vec4f>
#include "ptr.hpp"
#include "doorstate.hpp"
namespace ESM
{
@ -350,10 +351,9 @@ namespace MWWorld
virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const;
/// 0 = nothing, 1 = opening, 2 = closing
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
virtual DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead.
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
virtual void setDoorState (const MWWorld::Ptr &ptr, DoorState state) const;
virtual void respawn (const MWWorld::Ptr& ptr) const {}

View File

@ -0,0 +1,14 @@
#ifndef GAME_MWWORLD_DOORSTATE_H
#define GAME_MWWORLD_DOORSTATE_H
namespace MWWorld
{
enum class DoorState
{
Idle = 0,
Opening = 1,
Closing = 2,
};
}
#endif

View File

@ -1458,6 +1458,9 @@ namespace MWWorld
{
mRendering->rotateObject(ptr, rotate);
mPhysics->updateRotation(ptr);
if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(object);
}
}
@ -1614,9 +1617,48 @@ namespace MWWorld
return result.mHit;
}
bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)
{
const ESM::Position& objPos = door.getRefData().getPosition();
float oldRot = objPos.rot[2];
float minRot = door.getCellRef().getPosition().rot[2];
float maxRot = minRot + osg::DegreesToRadians(90.f);
float diff = duration * osg::DegreesToRadians(90.f);
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot);
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot);
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
/// \todo should use convexSweepTest here
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
for (MWWorld::Ptr& ptr : collisions)
{
if (ptr.getClass().isActor())
{
// Collided with actor, ask actor to try to avoid door
if(ptr != getPlayerPtr() )
{
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(door),ptr);
}
// we need to undo the rotation
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
// the rotation order we want to use
mWorldScene->updateObjectRotation(door, false);
return reached;
}
void World::processDoors(float duration)
{
std::map<MWWorld::Ptr, int>::iterator it = mDoorStates.begin();
auto it = mDoorStates.begin();
while (it != mDoorStates.end())
{
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
@ -1628,45 +1670,12 @@ namespace MWWorld
}
else
{
const ESM::Position& objPos = it->first.getRefData().getPosition();
float oldRot = objPos.rot[2];
float minRot = it->first.getCellRef().getPosition().rot[2];
float maxRot = minRot + osg::DegreesToRadians(90.f);
float diff = duration * osg::DegreesToRadians(90.f);
float targetRot = std::min(std::max(minRot, oldRot + diff * (it->second == 1 ? 1 : -1)), maxRot);
rotateObject(it->first, objPos.rot[0], objPos.rot[1], targetRot);
bool reached = (targetRot == maxRot && it->second) || targetRot == minRot;
/// \todo should use convexSweepTest here
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
for (MWWorld::Ptr& ptr : collisions)
{
if (ptr.getClass().isActor())
{
// Collided with actor, ask actor to try to avoid door
if(ptr != getPlayerPtr() )
{
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
}
// we need to undo the rotation
rotateObject(it->first, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
// the rotation order we want to use
mWorldScene->updateObjectRotation(it->first, false);
bool reached = rotateDoor(it->first, it->second, duration);
if (reached)
{
// Mark as non-moving
it->first.getClass().setDoorState(it->first, 0);
it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle);
mDoorStates.erase(it++);
}
else
@ -2389,11 +2398,6 @@ namespace MWWorld
mRendering->allowVanityMode(allow);
}
void World::togglePlayerLooking(bool enable)
{
mRendering->togglePlayerLooking(enable);
}
void World::changeVanityModeScale(float factor)
{
mRendering->changeVanityModeScale(factor);
@ -2506,33 +2510,36 @@ namespace MWWorld
void World::activateDoor(const MWWorld::Ptr& door)
{
int state = door.getClass().getDoorState(door);
auto state = door.getClass().getDoorState(door);
switch (state)
{
case 0:
case MWWorld::DoorState::Idle:
if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2])
state = 1; // if closed, then open
state = MWWorld::DoorState::Opening; // if closed, then open
else
state = 2; // if open, then close
state = MWWorld::DoorState::Closing; // if open, then close
break;
case 2:
state = 1; // if closing, then open
case MWWorld::DoorState::Closing:
state = MWWorld::DoorState::Opening; // if closing, then open
break;
case 1:
case MWWorld::DoorState::Opening:
default:
state = 2; // if opening, then close
state = MWWorld::DoorState::Closing; // if opening, then close
break;
}
door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
}
void World::activateDoor(const Ptr &door, int state)
void World::activateDoor(const Ptr &door, MWWorld::DoorState state)
{
door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
if (state == 0)
if (state == MWWorld::DoorState::Idle)
{
mDoorStates.erase(door);
rotateDoor(door, state, 1);
}
}
bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object)

View File

@ -118,7 +118,7 @@ namespace MWWorld
int mActivationDistanceOverride;
std::map<MWWorld::Ptr, int> mDoorStates;
std::map<MWWorld::Ptr, MWWorld::DoorState> mDoorStates;
///< only holds doors that are currently moving. 1 = opening, 2 = closing
std::string mStartCell;
@ -146,6 +146,8 @@ namespace MWWorld
private:
void PCDropped (const Ptr& item);
bool rotateDoor(const Ptr door, DoorState state, float duration);
void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly.
@ -526,8 +528,6 @@ namespace MWWorld
void allowVanityMode(bool allow) override;
void togglePlayerLooking(bool enable) override;
void changeVanityModeScale(float factor) override;
bool vanityRotateCamera(float * rot) override;
@ -542,7 +542,7 @@ namespace MWWorld
/// update movement state of a non-teleport door as specified
/// @param state see MWClass::setDoorState
/// @note throws an exception when invoked on a teleport door
void activateDoor(const MWWorld::Ptr& door, int state) override;
void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) override;
void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) override; ///< 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

View File

@ -163,22 +163,23 @@ void NiPixelData::read(NIFStream *nif)
{
fmt = (Format)nif->getUInt();
rmask = nif->getInt(); // usually 0xff
gmask = nif->getInt(); // usually 0xff00
bmask = nif->getInt(); // usually 0xff0000
amask = nif->getInt(); // usually 0xff000000 or zero
rmask = nif->getUInt(); // usually 0xff
gmask = nif->getUInt(); // usually 0xff00
bmask = nif->getUInt(); // usually 0xff0000
amask = nif->getUInt(); // usually 0xff000000 or zero
bpp = nif->getInt();
bpp = nif->getUInt();
// Unknown
nif->skip(12);
// 8 bytes of "Old Fast Compare". Whatever that means.
nif->skip(8);
palette.read(nif);
numberOfMipmaps = nif->getInt();
numberOfMipmaps = nif->getUInt();
// Bytes per pixel, should be bpp * 8
/* int bytes = */ nif->getInt();
/* int bytes = */ nif->getUInt();
for(int i=0; i<numberOfMipmaps; i++)
for(unsigned int i=0; i<numberOfMipmaps; i++)
{
// Image size and offset in the following data field
Mipmap m;
@ -189,12 +190,17 @@ void NiPixelData::read(NIFStream *nif)
}
// Read the data
unsigned int dataSize = nif->getInt();
unsigned int dataSize = nif->getUInt();
data.reserve(dataSize);
for (unsigned i=0; i<dataSize; ++i)
data.push_back((unsigned char)nif->getChar());
}
void NiPixelData::post(NIFFile *nif)
{
palette.post(nif);
}
void NiColorData::read(NIFStream *nif)
{
mKeyMap = std::make_shared<Vector4KeyMap>();
@ -278,4 +284,14 @@ void NiKeyframeData::read(NIFStream *nif)
mScales->read(nif);
}
void NiPalette::read(NIFStream *nif)
{
unsigned int alphaMask = !nif->getChar() ? 0xFF000000 : 0;
// Fill the entire palette with black even if there isn't enough entries.
colors.resize(256);
unsigned int numEntries = nif->getUInt();
for (unsigned int i = 0; i < numEntries; i++)
colors[i] = nif->getUInt() | alphaMask;
}
} // Namespace

View File

@ -116,6 +116,7 @@ public:
NIPXFMT_RGB8,
NIPXFMT_RGBA8,
NIPXFMT_PAL8,
NIPXFMT_PALA8,
NIPXFMT_DXT1,
NIPXFMT_DXT3,
NIPXFMT_DXT5,
@ -123,8 +124,10 @@ public:
};
Format fmt;
unsigned int rmask, gmask, bmask, amask;
int bpp, numberOfMipmaps;
unsigned int rmask, gmask, bmask, amask, bpp;
NiPalettePtr palette;
unsigned int numberOfMipmaps;
struct Mipmap
{
@ -136,6 +139,7 @@ public:
std::vector<unsigned char> data;
void read(NIFStream *nif);
void post(NIFFile *nif);
};
class NiColorData : public Record
@ -219,5 +223,14 @@ struct NiKeyframeData : public Record
void read(NIFStream *nif);
};
class NiPalette : public Record
{
public:
// 32-bit RGBA colors that correspond to 8-bit indices
std::vector<unsigned int> colors;
void read(NIFStream *nif);
};
} // Namespace
#endif

View File

@ -112,6 +112,7 @@ static std::map<std::string,RecordFactoryEntry> makeFactory()
newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture ));
newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance ));
newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController ));
newFactory.insert(makeEntry("NiPalette", &construct <NiPalette> , RC_NiPalette ));
return newFactory;
}

View File

@ -97,7 +97,8 @@ enum RecordType
RC_NiSkinInstance,
RC_RootCollisionNode,
RC_NiSphericalCollider,
RC_NiLookAtController
RC_NiLookAtController,
RC_NiPalette
};
/// Base class for all records

View File

@ -140,6 +140,7 @@ class NiSkinInstance;
class NiSourceTexture;
class NiRotatingParticlesData;
class NiAutoNormalParticlesData;
class NiPalette;
typedef RecordPtrT<Node> NodePtr;
typedef RecordPtrT<Extra> ExtraPtr;
@ -160,6 +161,7 @@ typedef RecordPtrT<NiSkinInstance> NiSkinInstancePtr;
typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr;
typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr;
typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordPtrT<NiPalette> NiPalettePtr;
typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList;

View File

@ -408,8 +408,8 @@ namespace NifOsg
unsigned int clamp = static_cast<unsigned int>(textureEffect->clamp);
int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1;
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::TexEnvCombine> texEnv = new osg::TexEnvCombine;
texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
@ -777,8 +777,8 @@ namespace NifOsg
// inherit wrap settings from the target slot
osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE));
osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP;
osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP;
osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP_TO_EDGE;
osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP_TO_EDGE;
if (inherit)
{
wrapS = inherit->getWrap(osg::Texture2D::WRAP_S);
@ -1072,6 +1072,8 @@ namespace NifOsg
if (nifNode->recType == Nif::RC_NiTriShape)
{
const Nif::NiTriShape* triShape = static_cast<const Nif::NiTriShape*>(nifNode);
if (!triShape->data.empty())
{
const Nif::NiTriShapeData* data = triShape->data.getPtr();
vertexColorsPresent = !data->colors.empty();
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name);
@ -1079,9 +1081,12 @@ namespace NifOsg
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(),
(unsigned short*)data->triangles.data()));
}
}
else
{
const Nif::NiTriStrips* triStrips = static_cast<const Nif::NiTriStrips*>(nifNode);
if (!triStrips->data.empty())
{
const Nif::NiTriStripsData* data = triStrips->data.getPtr();
vertexColorsPresent = !data->colors.empty();
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name);
@ -1097,6 +1102,7 @@ namespace NifOsg
}
}
}
}
// osg::Material properties are handled here for two reasons:
// - if there are no vertex colors, we need to disable colorMode.
@ -1276,9 +1282,11 @@ namespace NifOsg
switch (pixelData->fmt)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_PAL8:
pixelformat = GL_RGB;
break;
case Nif::NiPixelData::NIPXFMT_RGBA8:
case Nif::NiPixelData::NIPXFMT_PALA8:
pixelformat = GL_RGBA;
break;
default:
@ -1293,7 +1301,7 @@ namespace NifOsg
int height = 0;
std::vector<unsigned int> mipmapVector;
for (unsigned int i=0; i<pixelData->mipmaps.size()-3; ++i)
for (unsigned int i=0; i<pixelData->mipmaps.size(); ++i)
{
const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i];
@ -1319,10 +1327,59 @@ namespace NifOsg
return nullptr;
}
unsigned char* data = new unsigned char[pixelData->data.size()];
memcpy(data, pixelData->data.data(), pixelData->data.size());
const std::vector<unsigned char>& pixels = pixelData->data;
switch (pixelData->fmt)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_RGBA8:
{
unsigned char* data = new unsigned char[pixels.size()];
memcpy(data, pixels.data(), pixels.size());
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
break;
}
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
{
if (pixelData->palette.empty() || pixelData->bpp != 8)
{
Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring";
return nullptr;
}
// We're going to convert the indices that pixel data contains
// into real colors using the palette.
const std::vector<unsigned int>& palette = pixelData->palette->colors;
if (pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8)
{
unsigned char* data = new unsigned char[pixels.size() * 3];
for (size_t i = 0; i < pixels.size(); i++)
{
unsigned int color = palette[pixels[i]];
data[i * 3 + 0] = (color >> 0) & 0xFF;
data[i * 3 + 1] = (color >> 8) & 0xFF;
data[i * 3 + 2] = (color >> 16) & 0xFF;
}
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
}
else // if (fmt = NIPXFMT_PALA8)
{
unsigned char* data = new unsigned char[pixels.size() * 4];
for (size_t i = 0; i < pixels.size(); i++)
{
unsigned int color = palette[pixels[i]];
data[i * 4 + 0] = (color >> 0) & 0xFF;
data[i * 4 + 1] = (color >> 8) & 0xFF;
data[i * 4 + 2] = (color >> 16) & 0xFF;
data[i * 4 + 3] = (color >> 24) & 0xFF;
}
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
}
break;
}
default:
return nullptr;
}
image->setMipmapLevels(mipmapVector);
image->flipVertical();
@ -1392,8 +1449,8 @@ namespace NifOsg
int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1;
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
int texUnit = boundTextures.size();