Merge pull request #359 from OpenMW/master while resolving conflicts

# Conflicts:
#	.travis.yml
0.6.1-openmw-updates
David Cernat 6 years ago
commit ecb0ad0d77

@ -24,7 +24,7 @@ addons:
- llvm-toolchain-precise-3.6
packages: [
# Dev
clang-3.6, libunshield-dev, libtinyxml-dev,
cmake, clang-3.6, libunshield-dev, libtinyxml-dev,
g++-6,
# Tests
libgtest-dev, google-mock,

@ -359,6 +359,7 @@ if (NOT WIN32 AND NOT APPLE)
endif()
# CXX Compiler settings
set(CMAKE_CXX_STANDARD 11)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++11 -pedantic -Wno-long-long")
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )

@ -33,6 +33,7 @@ Furthermore, we advise to:
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.
Guidelines for original engine "fixes"
=================================

@ -169,18 +169,25 @@ void CSMPrefs::State::declare()
"list go to the first/last item");
declareCategory ("3D Scene Input");
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareSeparator();
declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareSeparator();
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0);
declareBool ("navi-orbit-const-roll", "Keep camera roll constant for orbital camera", true);
declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false);
declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0).
setRange (0.001, 100.0);
@ -191,6 +198,13 @@ void CSMPrefs::State::declare()
setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0);
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Rendering");
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool ("camera-ortho", "Orthographic projection for camera", false);
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).
setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.").
setRange(10, 10000);
declareDouble ("object-marker-alpha", "Object Marker Transparency", 0.5).setPrecision(2).setRange(0,1);
declareCategory ("Tooltips");

@ -84,7 +84,6 @@ CSVPrefs::Dialogue::~Dialogue()
void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event)
{
QMainWindow::closeEvent (event);
CSMPrefs::State::get().save();
}

@ -668,16 +668,25 @@ namespace CSVRender
mInitialized = true;
}
void OrbitCameraController::setConstRoll(bool enabled)
{
mConstRoll = enabled;
}
void OrbitCameraController::rotateHorizontal(double value)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d absoluteUp = osg::Vec3(0,0,1);
osg::Quat rotation = osg::Quat(value, up);
osg::Quat rotation = osg::Quat(value, mConstRoll ? absoluteUp : up);
osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll)
up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
}
@ -687,9 +696,14 @@ namespace CSVRender
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Quat rotation = osg::Quat(value, up ^ forward);
osg::Vec3d axis = up ^ forward;
osg::Quat rotation = osg::Quat(value,axis);
osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll)
up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
}

@ -160,6 +160,8 @@ namespace CSVRender
/// \brief Flag controller to be re-initialized.
void reset();
void setConstRoll(bool enable);
private:
void initialize();
@ -181,6 +183,8 @@ namespace CSVRender
double mOrbitSpeed;
double mOrbitSpeedMult;
bool mConstRoll;
private slots:
void naviPrimary(bool active);

@ -471,7 +471,7 @@ void CSVRender::Object::setSelected(bool selected)
else
mRootNode->addChild(mBaseNode);
mMarkerTransparency = CSMPrefs::get()["3D Scene Input"]["object-marker-alpha"].toDouble();
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
updateMarker();
}

@ -56,6 +56,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
traits->vsync = false;
mView = new osgViewer::View;
updateCameraParameters( traits->width / static_cast<double>(traits->height) );
osg::ref_ptr<osgQt::GraphicsWindowQt> window = new osgQt::GraphicsWindowQt(traits.get());
QLayout* layout = new QHBoxLayout(this);
@ -66,7 +67,6 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
mView->getCamera()->setGraphicsContext(window);
mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) );
mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
mView->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0f, 10000.0f );
SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;
lightMgr->setStartLight(1);
@ -188,6 +188,8 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);
mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() );
// we handle lighting manually
mView->setLightingMode(osgViewer::View::NO_LIGHT);
@ -370,6 +372,40 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
{
mOrbitCamControl->setOrbitSpeedMultiplier(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-orbit-const-roll")
{
mOrbitCamControl->setConstRoll(setting->isTrue());
}
else if (*setting=="Rendering/camera-fov" ||
*setting=="Rendering/camera-ortho" ||
*setting=="Rendering/camera-ortho-size")
{
updateCameraParameters();
}
}
void RenderWidget::updateCameraParameters(double overrideAspect)
{
const float nearDist = 1.0;
const float farDist = 1000.0;
if (CSMPrefs::get()["Rendering"]["camera-ortho"].isTrue())
{
const float size = CSMPrefs::get()["Rendering"]["camera-ortho-size"].toInt();
const float aspect = overrideAspect >= 0.0 ? overrideAspect : (width() / static_cast<double>(height()));
const float halfH = size * 10.0;
const float halfW = halfH * aspect;
mView->getCamera()->setProjectionMatrixAsOrtho(
-halfW, halfW, -halfH, halfH, nearDist, farDist);
}
else
{
mView->getCamera()->setProjectionMatrixAsPerspective(
CSMPrefs::get()["Rendering"]["camera-fov"].toInt(),
static_cast<double>(width())/static_cast<double>(height()),
nearDist, farDist);
}
}
void SceneWidget::selectNavigationMode (const std::string& mode)

@ -64,6 +64,8 @@ namespace CSVRender
osg::ref_ptr<osgViewer::View> mView;
osg::ref_ptr<osg::Group> mRootNode;
void updateCameraParameters(double overrideAspect = -1.0);
QTimer mTimer;
protected slots:

@ -131,7 +131,7 @@ void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setti
mDragWheelFactor = setting->toDouble();
else if (*setting=="3D Scene Input/drag-shift-factor")
mDragShiftFactor = setting->toDouble();
else if (*setting=="3D Scene Input/object-marker-alpha" && !mInConstructor)
else if (*setting=="Rendering/object-marker-alpha" && !mInConstructor)
{
float alpha = setting->toDouble();
// getSelection is virtual, thus this can not be called from the constructor

@ -1265,6 +1265,15 @@ namespace MWClass
else
{
ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr);
// Take in account armor condition
const bool hasHealth = it->getClass().hasItemHealth(*it);
if (hasHealth)
{
int armorHealth = it->getClass().getItemHealth(*it);
int armorMaxHealth = it->getClass().getItemMaxHealth(*it);
ratings[i] *= (float(armorHealth) / armorMaxHealth);
}
}
}

@ -184,7 +184,6 @@ namespace MWDialogue
// TODO play sound
}
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
callback->addResponse("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript, mActor);
@ -425,7 +424,7 @@ namespace MWDialogue
{
Filter filter (mActor, mChoice, mTalkedTo);
if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
{
if (const ESM::DialInfo *info = filter.search (*dialogue, true))
{
@ -439,15 +438,18 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
callback->addResponse("", Interpreter::fixDefinesDialog(text, interpreterContext));
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=dialogue->mInfo.end(); ++iter)
if (dialogue->mType == ESM::Dialogue::Topic)
{
if (iter->mId == info->mId)
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=dialogue->mInfo.end(); ++iter)
{
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor);
break;
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor);
break;
}
}
}

@ -281,6 +281,8 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
textColours.journalTopicOver,
textColours.journalTopicPressed, first);
ch[1]++;
// Words can not be started with these characters
if (i == 26 || i == 28)
continue;
@ -290,8 +292,6 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
typesetter->write (style, to_utf8_span (buffer));
typesetter->lineBreak ();
ch[1]++;
}
return typesetter;

@ -1069,7 +1069,8 @@ namespace MWMechanics
if (player.getClass().getNpcStats(player).isWerewolf())
return;
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat())
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
{
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt();

@ -1710,6 +1710,12 @@ namespace MWMechanics
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
{
// Don't become aggressive if a calm effect is active, since it would cause combat to cycle on/off as
// combat is activated here and then canceled by the calm effect
if ((ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0)
|| (!ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0))
return false;
int disposition = 50;
if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr, true);

@ -1026,9 +1026,9 @@ namespace MWMechanics
float y = roll / std::min(x, 100.f);
y *= 0.25f * x;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
effect.mDuration = static_cast<int>(y);
else
effect.mDuration = 1;
else
effect.mDuration = static_cast<int>(y);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
{
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))

@ -625,7 +625,7 @@ namespace MWRender
float Animation::getStartTime(const std::string &groupname) const
{
for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter)
for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();
@ -638,7 +638,7 @@ namespace MWRender
float Animation::getTextKeyTime(const std::string &textKey) const
{
for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter)
for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();

@ -232,11 +232,6 @@ namespace MWScript
if (!ptr.isInCell())
return;
if (ptr == MWMechanics::getPlayer())
{
MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true);
}
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Float pos = runtime[0].mFloat;
@ -246,6 +241,8 @@ namespace MWScript
float ay = ptr.getRefData().getPosition().pos[1];
float az = ptr.getRefData().getPosition().pos[2];
// Note: SetPos does not skip weather transitions in vanilla engine, so we do not call setTeleported(true) here.
MWWorld::Ptr updated = ptr;
if(axis == "x")
{
@ -257,6 +254,17 @@ namespace MWScript
}
else if(axis == "z")
{
// We should not place actors under ground
if (ptr.getClass().isActor())
{
float terrainHeight = -std::numeric_limits<float>::max();
if (ptr.getCell()->isExterior())
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(osg::Vec3f(ax, ay, az));
if (pos < terrainHeight)
pos = terrainHeight;
}
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos);
}
else

@ -3,7 +3,7 @@
#include <memory>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <components/vfs/manager.hpp>
@ -11,11 +11,6 @@
namespace MWSound
{
void FFmpeg_Decoder::fail(const std::string &msg)
{
throw std::runtime_error("FFmpeg exception: "+msg);
}
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{
try
@ -33,7 +28,8 @@ int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
int FFmpeg_Decoder::writePacket(void *, uint8_t *, int)
{
throw std::runtime_error("can't write to read-only stream");
std::cerr<< "can't write to read-only stream" <<std::endl;
return -1;
}
int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
@ -186,34 +182,34 @@ void FFmpeg_Decoder::open(const std::string &fname)
mDataStream = mResourceMgr->get(fname);
if((mFormatCtx=avformat_alloc_context()) == NULL)
fail("Failed to allocate context");
throw std::runtime_error("Failed to allocate context");
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
try
{
// "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{
if (mFormatCtx->pb != NULL)
// "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{
if (mFormatCtx->pb->buffer != NULL)
if (mFormatCtx->pb != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
avformat_free_context(mFormatCtx);
}
avformat_free_context(mFormatCtx);
mFormatCtx = NULL;
throw std::runtime_error("Failed to allocate input stream");
}
mFormatCtx = NULL;
fail("Failed to allocate input stream");
}
try
{
if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname);
throw std::runtime_error("Failed to find stream info in "+fname);
for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
{
@ -224,32 +220,46 @@ void FFmpeg_Decoder::open(const std::string &fname)
}
}
if(!mStream)
fail("No audio streams in "+fname);
throw std::runtime_error("No audio streams in "+fname);
(*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt;
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << (*mStream)->codec->codec_id;
fail(ss.str());
std::string ss = "No codec found for id " +
std::to_string((*mStream)->codec->codec_id);
throw std::runtime_error(ss);
}
if(avcodec_open2((*mStream)->codec, codec, NULL) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
throw std::runtime_error(std::string("Failed to open audio codec ") +
codec->long_name);
mFrame = av_frame_alloc();
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT ||
(*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
mOutputChannelLayout = (*mStream)->codec->channel_layout;
if(mOutputChannelLayout == 0)
mOutputChannelLayout = av_get_default_channel_layout((*mStream)->codec->channels);
}
catch(std::exception&)
{
catch(...) {
if(mStream)
avcodec_close((*mStream)->codec);
mStream = NULL;
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
@ -274,18 +284,18 @@ void FFmpeg_Decoder::close()
{
if (mFormatCtx->pb != NULL)
{
// mFormatCtx->pb->buffer must be freed by hand,
// if not, valgrind will show memleak, see:
//
// https://trac.ffmpeg.org/ticket/1357
//
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
// mFormatCtx->pb->buffer must be freed by hand,
// if not, valgrind will show memleak, see:
//
// https://trac.ffmpeg.org/ticket/1357
//
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
}
avformat_close_input(&mFormatCtx);
}
@ -301,16 +311,7 @@ std::string FFmpeg_Decoder::getName()
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{
if(!mStream)
fail("No audio stream info");
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
throw std::runtime_error("No audio stream info");
if(mOutputSampleFormat == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8;
@ -318,19 +319,11 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
*type = SampleType_Int16;
else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT)
*type = SampleType_Float32;
int64_t ch_layout = (*mStream)->codec->channel_layout;
if(ch_layout == 0)
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels);
mOutputChannelLayout = ch_layout;
if (ch_layout == AV_CH_LAYOUT_5POINT1 || ch_layout == AV_CH_LAYOUT_7POINT1
|| ch_layout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
else if (ch_layout != AV_CH_LAYOUT_MONO
&& ch_layout != AV_CH_LAYOUT_STEREO)
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
else
{
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
*type = SampleType_Int16;
}
if(mOutputChannelLayout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono;
@ -347,13 +340,27 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
char str[1024];
av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels,
(*mStream)->codec->channel_layout);
fail(std::string("Unsupported channel layout: ")+str);
std::cerr<< "Unsupported channel layout: "<<str <<std::endl;
if((*mStream)->codec->channels == 1)
{
mOutputChannelLayout = AV_CH_LAYOUT_MONO;
*chans = ChannelConfig_Mono;
}
else
{
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
*chans = ChannelConfig_Stereo;
}
}
*samplerate = (*mStream)->codec->sample_rate;
int64_t ch_layout = (*mStream)->codec->channel_layout;
if(ch_layout == 0)
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels);
if(mOutputSampleFormat != (*mStream)->codec->sample_fmt
|| mOutputChannelLayout != ch_layout)
if(mOutputSampleFormat != (*mStream)->codec->sample_fmt ||
mOutputChannelLayout != ch_layout)
{
mSwr = swr_alloc_set_opts(mSwr, // SwrContext
mOutputChannelLayout, // output ch layout
@ -365,24 +372,29 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
0, // logging level offset
NULL); // log context
if(!mSwr)
fail(std::string("Couldn't allocate SwrContext"));
throw std::runtime_error("Couldn't allocate SwrContext");
if(swr_init(mSwr) < 0)
fail(std::string("Couldn't initialize SwrContext"));
throw std::runtime_error("Couldn't initialize SwrContext");
}
}
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{
if(!mStream)
fail("No audio stream");
{
std::cerr<< "No audio stream" <<std::endl;
return 0;
}
return readAVAudioData(buffer, bytes);
}
void FFmpeg_Decoder::readAll(std::vector<char> &output)
{
if(!mStream)
fail("No audio stream");
{
std::cerr<< "No audio stream" <<std::endl;
return;
}
while(getAVAudioData())
{

@ -35,7 +35,7 @@ extern "C"
namespace MWSound
{
class FFmpeg_Decoder : public Sound_Decoder
class FFmpeg_Decoder final : public Sound_Decoder
{
AVFormatContext *mFormatCtx;
AVStream **mStream;
@ -66,17 +66,15 @@ namespace MWSound
bool getAVAudioData();
size_t readAVAudioData(void *data, size_t length);
virtual void open(const std::string &fname);
virtual void close();
void open(const std::string &fname) override;
void close() override;
virtual std::string getName();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
std::string getName() override;
void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
virtual size_t read(char *buffer, size_t bytes);
virtual void readAll(std::vector<char> &output);
virtual size_t getSampleOffset();
void fail(const std::string &msg);
size_t read(char *buffer, size_t bytes) override;
void readAll(std::vector<char> &output) override;
size_t getSampleOffset() override;
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs);

@ -1,5 +1,7 @@
#include "movieaudiofactory.hpp"
#include <iostream>
#include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp>
#include <extern/osg-ffmpeg-videoplayer/videostate.hpp>
@ -13,7 +15,7 @@ namespace MWSound
{
class MovieAudioDecoder;
class MWSoundDecoderBridge : public Sound_Decoder
class MWSoundDecoderBridge final : public Sound_Decoder
{
public:
MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder)
@ -25,12 +27,12 @@ namespace MWSound
private:
MWSound::MovieAudioDecoder* mDecoder;
virtual void open(const std::string &fname);
virtual void close();
virtual std::string getName();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes);
virtual size_t getSampleOffset();
void open(const std::string &fname) override;
void close() override;
std::string getName() override;
void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
size_t read(char *buffer, size_t bytes) override;
size_t getSampleOffset() override;
};
class MovieAudioDecoder : public Video::MovieAudioDecoder
@ -98,7 +100,7 @@ namespace MWSound
void MWSoundDecoderBridge::open(const std::string &fname)
{
throw std::runtime_error("unimplemented");
throw std::runtime_error("Method not implemented");
}
void MWSoundDecoderBridge::close() {}
@ -123,11 +125,8 @@ namespace MWSound
else if (outputChannelLayout == AV_CH_LAYOUT_QUAD)
*chans = ChannelConfig_Quad;
else
{
std::stringstream error;
error << "Unsupported channel layout: " << outputChannelLayout;
throw std::runtime_error(error.str());
}
throw std::runtime_error("Unsupported channel layout: "+
std::to_string(outputChannelLayout));
AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat();
if (outputSampleFormat == AV_SAMPLE_FMT_U8)
@ -140,7 +139,7 @@ namespace MWSound
{
char str[1024];
av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat);
throw std::runtime_error(std::string("Unsupported sample format: ") + str);
throw std::runtime_error(std::string("Unsupported sample format: ")+str);
}
}

@ -411,9 +411,14 @@ bool OpenAL_SoundStream::init(bool getLoudnessData)
ChannelConfig chans;
SampleType type;
mDecoder->getInfo(&mSampleRate, &chans, &type);
mFormat = getALFormat(chans, type);
if(!mFormat) return false;
try {
mDecoder->getInfo(&mSampleRate, &chans, &type);
mFormat = getALFormat(chans, type);
}
catch(std::exception &e) {
std::cerr<< "Failed to get stream info: "<<e.what() <<std::endl;
return false;
}
switch(type)
{
@ -945,31 +950,41 @@ std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname
{
getALError();
DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
if(decoder->mResourceMgr->exists(fname))
decoder->open(fname);
else
{
std::string file = fname;
std::string::size_type pos = file.rfind('.');
if(pos != std::string::npos)
file = file.substr(0, pos)+".mp3";
decoder->open(file);
}
std::vector<char> data;
ChannelConfig chans;
SampleType type;
ALenum format;
int srate;
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
if(!format) return std::make_pair(nullptr, 0);
try {
DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
if(decoder->mResourceMgr->exists(fname))
decoder->open(fname);
else
{
std::string file = fname;
std::string::size_type pos = file.rfind('.');
if(pos != std::string::npos)
file = file.substr(0, pos)+".mp3";
decoder->open(file);
}
ChannelConfig chans;
SampleType type;
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
if(format) decoder->readAll(data);
}
catch(std::exception &e) {
std::cerr<< "Failed to load audio from "<<fname<<": "<<e.what() <<std::endl;
}
decoder->readAll(data);
decoder->close();
if(data.empty())
{
// If we failed to get any usable audio, substitute with silence.
format = AL_FORMAT_MONO8;
srate = 8000;
data.assign(8000, -128);
}
ALint size;
ALuint buf = 0;

@ -2607,6 +2607,10 @@ namespace MWWorld
if (ptr.getRefData().isDeleted())
return true;
// vanilla Morrowind does not allow to sell items from containers with zero capacity
if (ptr.getClass().getCapacity(ptr) <= 0.f)
return true;
if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId()))
mOut.push_back(ptr);

Loading…
Cancel
Save