diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 9fe947b68..f38f0cde0 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -120,6 +120,11 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() reuse->setToolTip ("When a new subview is requested and a matching subview already " " exist, do not open a new subview and use the existing one instead."); + Setting *statusBar = createSetting (Type_CheckBox, "show-statusbar", "Show Status Bar"); + statusBar->setDefaultValue ("true"); + statusBar->setToolTip ("If a newly open top level window is showing status bars or not. " + " Note that this does not affect existing windows."); + Setting *maxSubView = createSetting (Type_SpinBox, "max-subviews", "Maximum number of subviews per top-level window"); maxSubView->setDefaultValue (256); @@ -127,6 +132,12 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() maxSubView->setToolTip ("If the maximum number is reached and a new subview is opened " "it will be placed into a new top-level window."); + Setting *hide = createSetting (Type_CheckBox, "hide-subview", "Hide single subview"); + hide->setDefaultValue ("false"); + hide->setToolTip ("When a view contains only a single subview, hide the subview title " + "bar and if this subview is closed also close the view (unless it is the last " + "view for this document)"); + Setting *minWidth = createSetting (Type_SpinBox, "minimum-width", "Minimum subview width"); minWidth->setDefaultValue (325); @@ -158,6 +169,53 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() "This option may be removed in future once the mouse picking feature is completed."); } + declareSection ("table-input", "Table Input"); + { + QString inPlaceEdit ("Edit in Place"); + QString editRecord ("Edit Record"); + QString view ("View"); + QString editRecordAndClose ("Edit Record and Close"); + + QStringList values; + values + << "None" << inPlaceEdit << editRecord << view << "Revert" << "Delete" + << editRecordAndClose << "View and Close"; + + QString toolTip = ""; + + Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click"); + doubleClick->setDeclaredValues (values); + doubleClick->setDefaultValue (inPlaceEdit); + doubleClick->setToolTip ("Action on double click in table:

" + toolTip); + + Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s", + "Shift Double Click"); + shiftDoubleClick->setDeclaredValues (values); + shiftDoubleClick->setDefaultValue (editRecord); + shiftDoubleClick->setToolTip ("Action on shift double click in table:

" + toolTip); + + Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c", + "Control Double Click"); + ctrlDoubleClick->setDeclaredValues (values); + ctrlDoubleClick->setDefaultValue (view); + ctrlDoubleClick->setToolTip ("Action on control double click in table:

" + toolTip); + + Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc", + "Shift Control Double Click"); + shiftCtrlDoubleClick->setDeclaredValues (values); + shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose); + shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:

" + toolTip); + } + { /****************************************************************** * There are three types of values: diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index ab4325cfc..a9dce25be 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -3,7 +3,7 @@ #include "view.hpp" CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) - : mUniversalId (id), mParent (NULL) + : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view @@ -31,7 +31,15 @@ void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id) void CSVDoc::SubView::closeEvent (QCloseEvent *event) { - // update title bars of view and subviews - if(mParent) - mParent->updateSubViewIndicies(this); + emit updateSubViewIndicies (this); } + +std::string CSVDoc::SubView::getTitle() const +{ + return mUniversalId.toString(); +} + +void CSVDoc::SubView::closeRequest() +{ + emit closeRequest (this); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 6a3ddd8bd..a8aa3cda1 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -25,12 +25,13 @@ namespace CSVDoc Q_OBJECT CSMWorld::UniversalId mUniversalId; - View *mParent; // not implemented SubView (const SubView&); SubView& operator= (SubView&); + protected: + void setUniversalId(const CSMWorld::UniversalId& id); public: @@ -47,7 +48,9 @@ namespace CSVDoc virtual void useHint (const std::string& hint); ///< Default implementation: ignored - void setParent(View *parent) { mParent = parent; } + virtual std::string getTitle() const; + + virtual void updateUserSetting (const QString& name, const QStringList& value); private: @@ -57,9 +60,15 @@ namespace CSVDoc void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); - public slots: - virtual void updateUserSetting - (const QString &, const QStringList &); + void closeRequest (SubView *subView); + + void updateTitle(); + + void updateSubViewIndicies (SubView *view = 0); + + protected slots: + + void closeRequest(); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index d36e01901..8a6665cf2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -105,7 +105,7 @@ void CSVDoc::View::setupViewMenu() mShowStatusBar->setCheckable (true); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString(); + CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString(); if(showStatusBar == "true") mShowStatusBar->setChecked(true); view->addAction (mShowStatusBar); @@ -300,7 +300,7 @@ void CSVDoc::View::setupUi() setupDebugMenu(); } -void CSVDoc::View::updateTitle(const std::string subview) +void CSVDoc::View::updateTitle() { std::ostringstream stream; @@ -312,8 +312,13 @@ void CSVDoc::View::updateTitle(const std::string subview) if (mViewTotal>1) stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; - if (subview != "") - stream << " - " << subview; + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" && + mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); + + if (hideTitle) + stream << " - " << mSubViews.at (0)->getTitle(); setWindowTitle (stream.str().c_str()); } @@ -323,24 +328,26 @@ void CSVDoc::View::updateSubViewIndicies(SubView *view) if(view && mSubViews.contains(view)) mSubViews.removeOne(view); - if(mSubViews.size() == 1) + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" && + mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); + + updateTitle(); + + foreach (SubView *subView, mSubViews) { - if(!mSubViews.at(0)->isFloating()) + if (!subView->isFloating()) { - mSubViews.at(0)->setTitleBarWidget(new QWidget(this)); - updateTitle(mSubViews.at(0)->getUniversalId().getTypeName().c_str()); - } - } - else - { - updateTitle(); - if(mSubViews.size() > 1) - { - foreach(SubView * sb, mSubViews) + if (hideTitle) { - QWidget * tb = sb->titleBarWidget(); - if(tb) delete tb; - sb->setTitleBarWidget(0); + subView->setTitleBarWidget (new QWidget (this)); + subView->setWindowTitle (QString::fromUtf8 (subView->getTitle().c_str())); + } + else + { + delete subView->titleBarWidget(); + subView->setTitleBarWidget (0); } } } @@ -513,10 +520,12 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); - connect (&CSMSettings::UserSettings::instance(), - SIGNAL (userSettingUpdated (const QString &, const QStringList &)), - view, - SLOT (updateUserSetting (const QString &, const QStringList &))); + connect (view, SIGNAL (closeRequest (SubView *)), this, SLOT (closeRequest (SubView *))); + + connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle())); + + connect (view, SIGNAL (updateSubViewIndicies (SubView *)), + this, SLOT (updateSubViewIndicies (SubView *))); view->show(); } @@ -729,9 +738,16 @@ void CSVDoc::View::resizeViewHeight (int height) resize (geometry().width(), height); } -void CSVDoc::View::updateUserSetting - (const QString &name, const QStringList &list) -{} +void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &list) +{ + if (name=="window/hide-subview") + updateSubViewIndicies (0); + + foreach (SubView *subView, mSubViews) + { + subView->updateUserSetting (name, list); + } +} void CSVDoc::View::toggleShowStatusBar (bool show) { @@ -761,3 +777,14 @@ void CSVDoc::View::stop() { mDocument->stopRunning(); } + +void CSVDoc::View::closeRequest (SubView *subView) +{ + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + if (mSubViews.size()>1 || mViewTotal<=1 || + userSettings.setting ("window/hide-subview", QString ("false"))!="true") + subView->deleteLater(); + else if (mViewManager.closeRequest (this)) + mViewManager.removeDocAndView (mDocument); +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index e3ada1f2e..55ea5ee51 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -75,8 +75,6 @@ namespace CSVDoc void setupUi(); - void updateTitle(const std::string subview = ""); - void updateActions(); void exitApplication(); @@ -114,9 +112,6 @@ namespace CSVDoc /// Function called by view manager when user preferences are updated void updateEditorSetting (const QString &, const QString &); - // called when subviews are added or removed - void updateSubViewIndicies(SubView *view = 0); - signals: void newGameRequest(); @@ -139,6 +134,11 @@ namespace CSVDoc void updateUserSetting (const QString &, const QStringList &); + void updateTitle(); + + // called when subviews are added or removed + void updateSubViewIndicies (SubView *view = 0); + private slots: void newView(); @@ -222,6 +222,8 @@ namespace CSVDoc void run (const std::string& profile, const std::string& startupInstruction = ""); void stop(); + + void closeRequest (SubView *subView); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 4229ab531..5f6b6b46a 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -144,7 +144,7 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) mViews.push_back (view); std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString(); + CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString(); view->toggleStatusBar (showStatusBar == "true"); view->show(); diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 1e106c69f..1ae466f42 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -9,7 +9,7 @@ #include "../widget/scenetoolmode.hpp" CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id) +: SubView (id), mTitle (id.toString().c_str()) { QHBoxLayout *layout = new QHBoxLayout; @@ -52,15 +52,19 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo void CSVWorld::PreviewSubView::setEditLock (bool locked) {} -void CSVWorld::PreviewSubView::closeRequest() +std::string CSVWorld::PreviewSubView::getTitle() const { - deleteLater(); + return mTitle; } void CSVWorld::PreviewSubView::referenceableIdChanged (const std::string& id) { if (id.empty()) - setWindowTitle ("Preview: Reference to "); + mTitle = "Preview: Reference to "; else - setWindowTitle (("Preview: Reference to " + id).c_str()); + mTitle = "Preview: Reference to " + id; + + setWindowTitle (QString::fromUtf8 (mTitle.c_str())); + + emit updateTitle(); } \ No newline at end of file diff --git a/apps/opencs/view/world/previewsubview.hpp b/apps/opencs/view/world/previewsubview.hpp index 4ca25c3cb..a28be5c36 100644 --- a/apps/opencs/view/world/previewsubview.hpp +++ b/apps/opencs/view/world/previewsubview.hpp @@ -20,6 +20,7 @@ namespace CSVWorld Q_OBJECT CSVRender::PreviewWidget *mScene; + std::string mTitle; public: @@ -27,9 +28,9 @@ namespace CSVWorld virtual void setEditLock (bool locked); - private slots: + virtual std::string getTitle() const; - void closeRequest(); + private slots: void referenceableIdChanged (const std::string& id); }; diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index d10eebb30..ce68da362 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -150,9 +150,9 @@ void CSVWorld::SceneSubView::useHint (const std::string& hint) mScene->useViewHint (hint); } -void CSVWorld::SceneSubView::closeRequest() +std::string CSVWorld::SceneSubView::getTitle() const { - deleteLater(); + return mTitle; } void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id) @@ -161,10 +161,11 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& std::ostringstream stream; stream << "Scene: " << getUniversalId().getId(); - setWindowTitle (QString::fromUtf8 (stream.str().c_str())); + mTitle = stream.str(); + setWindowTitle (QString::fromUtf8 (mTitle.c_str())); + emit updateTitle(); } - void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection) { setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, "sys::default")); @@ -189,7 +190,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection stream << "cell around it)"; } - setWindowTitle (QString::fromUtf8 (stream.str().c_str())); + mTitle = stream.str(); + setWindowTitle (QString::fromUtf8 (mTitle.c_str())); + emit updateTitle(); } void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data) diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index c0905f0d6..fc45347d0 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -44,6 +44,7 @@ namespace CSVWorld QHBoxLayout* mLayout; CSMDoc::Document& mDocument; CSVWidget::SceneToolbar* mToolbar; + std::string mTitle; public: @@ -57,6 +58,8 @@ namespace CSVWorld virtual void useHint (const std::string& hint); + virtual std::string getTitle() const; + private: void makeConnections(CSVRender::PagedWorldspaceWidget* widget); @@ -75,8 +78,6 @@ namespace CSVWorld private slots: - void closeRequest(); - void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::UniversalId& id); diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index df3fd87be..22d8e7e51 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -81,6 +81,6 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (!parent.isValid() && index.row()>=start && index.row()<=end) - deleteLater(); + emit closeRequest(); } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6adf3e0c1..ce53dcf5c 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -177,6 +177,79 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.exec (event->globalPos()); } +void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event) +{ + Qt::KeyboardModifiers modifiers = + event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier); + + QModelIndex index = currentIndex(); + + selectionModel()->select (index, + QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); + + std::map::iterator iter = + mDoubleClickActions.find (modifiers); + + if (iter==mDoubleClickActions.end()) + { + event->accept(); + return; + } + + switch (iter->second) + { + case Action_None: + + event->accept(); + break; + + case Action_InPlaceEdit: + + DragRecordTable::mouseDoubleClickEvent (event); + break; + + case Action_EditRecord: + + event->accept(); + editRecord(); + break; + + case Action_View: + + event->accept(); + viewRecord(); + break; + + case Action_Revert: + + event->accept(); + if (mDispatcher->canRevert()) + mDispatcher->executeRevert(); + break; + + case Action_Delete: + + event->accept(); + if (mDispatcher->canDelete()) + mDispatcher->executeDelete(); + break; + + case Action_EditRecordAndClose: + + event->accept(); + editRecord(); + emit closeRequest(); + break; + + case Action_ViewAndClose: + + event->accept(); + viewRecord(); + emit closeRequest(); + break; + } +} + CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document) : mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0), @@ -284,6 +357,11 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, this, SLOT (selectionSizeUpdate ())); setAcceptDrops(true); + + mDoubleClickActions.insert (std::make_pair (0, Action_InPlaceEdit)); + mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord)); + mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View)); + mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose)); } void CSVWorld::Table::setEditLock (bool locked) @@ -404,6 +482,9 @@ void CSVWorld::Table::editCell() void CSVWorld::Table::viewRecord() { + if (!(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View)) + return; + QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) @@ -439,18 +520,59 @@ void CSVWorld::Table::previewRecord() void CSVWorld::Table::updateUserSetting (const QString &name, const QStringList &list) { - int columns = mModel->columnCount(); + if (name=="records/type-format" || name=="records/status-format") + { + int columns = mModel->columnCount(); - for (int i=0; i - (*delegate).updateUserSetting (name, list); + for (int i=0; iindex (0, i), - mModel->index (mModel->rowCount()-1, i)); + dynamic_cast + (*delegate).updateUserSetting (name, list); + { + emit dataChanged (mModel->index (0, i), + mModel->index (mModel->rowCount()-1, i)); + } } - } + return; + } + + QString base ("table-input/double"); + if (name.startsWith (base)) + { + QString modifierString = name.mid (base.size()); + Qt::KeyboardModifiers modifiers = 0; + + if (modifierString=="-s") + modifiers = Qt::ShiftModifier; + else if (modifierString=="-c") + modifiers = Qt::ControlModifier; + else if (modifierString=="-sc") + modifiers = Qt::ShiftModifier | Qt::ControlModifier; + + DoubleClickAction action = Action_None; + + QString value = list.at (0); + + if (value=="Edit in Place") + action = Action_InPlaceEdit; + else if (value=="Edit Record") + action = Action_EditRecord; + else if (value=="View") + action = Action_View; + else if (value=="Revert") + action = Action_Revert; + else if (value=="Delete") + action = Action_Delete; + else if (value=="Edit Record and Close") + action = Action_EditRecordAndClose; + else if (value=="View and Close") + action = Action_ViewAndClose; + + mDoubleClickActions[modifiers] = action; + + return; + } } void CSVWorld::Table::tableSizeUpdate() @@ -567,7 +689,6 @@ std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const { - QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector idToDrag; diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index a80a0b362..75161b8b6 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -36,6 +36,18 @@ namespace CSVWorld { Q_OBJECT + enum DoubleClickAction + { + Action_None, + Action_InPlaceEdit, + Action_EditRecord, + Action_View, + Action_Revert, + Action_Delete, + Action_EditRecordAndClose, + Action_ViewAndClose + }; + std::vector mDelegates; QAction *mEditAction; QAction *mCreateAction; @@ -53,8 +65,8 @@ namespace CSVWorld CSMWorld::IdTableBase *mModel; int mRecordStatusDisplay; CSMWorld::CommandDispatcher *mDispatcher; - CSMWorld::UniversalId mEditCellId; + std::map mDoubleClickActions; private: @@ -64,6 +76,10 @@ namespace CSVWorld void dropEvent(QDropEvent *event); + protected: + + virtual void mouseDoubleClickEvent (QMouseEvent *event); + public: Table (const CSMWorld::UniversalId& id, bool createAndDelete, @@ -94,6 +110,8 @@ namespace CSVWorld void cloneRequest(const CSMWorld::UniversalId&); + void closeRequest(); + private slots: void editCell(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e2c8d5c1e..54518023b 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -69,6 +69,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect(mFilterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect (mTable, SIGNAL (closeRequest()), this, SLOT (closeRequest())); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -150,3 +152,4 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) } return false; } + diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 71a45a92c..c1a889913 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -199,6 +199,10 @@ namespace MWBase virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; ///< Search is limited to the active cells. + virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr) = 0; + ///< Return a pointer to a liveCellRef which contains \a ptr. + /// \note Search is limited to the active cells. + /// \todo enable reference in the OGRE scene virtual void enable (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7b2d7e132..e6d266de2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -191,8 +191,11 @@ namespace MWClass std::string text; - text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + if (ref->mBase->mData.mWeight != 0) + { + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 3e21b62e2..5b7925057 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -132,6 +132,7 @@ namespace MWGui virtual int pageSplit(); protected: + virtual ~GraphicElement() {} MyGUI::Widget * mParent; Paginator & mPaginator; BlockStyle mBlockStyle; diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 76fdd5287..f8054925b 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -30,8 +30,7 @@ int VideoWidget::getVideoHeight() bool VideoWidget::update() { - mPlayer.update(); - return mPlayer.isPlaying(); + return mPlayer.update(); } void VideoWidget::stop() diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f431bc8f1..6ab8b94c5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -377,6 +377,7 @@ namespace MWGui delete mHitFader; delete mWerewolfFader; delete mScreenFader; + delete mBlindnessFader; delete mDebugWindow; cleanupGarbage(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 247a0ba14..fef9fa644 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -27,9 +27,9 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - addAnimSource(model); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); } } @@ -47,9 +47,9 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - addAnimSource(model); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index cb02a6c97..52021839d 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -21,6 +21,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" @@ -434,6 +435,16 @@ namespace MWScript else ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); + if (ref2.getContainerStore()) // is the object contained? + { + MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2); + + if (!container.isEmpty()) + ref2 = container; + else + throw std::runtime_error("failed to find container ptr"); + } + const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false); // If the objects are in different worldspaces, return a large value (just like vanilla) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 0de8f3b91..c7d221139 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -454,7 +454,8 @@ namespace MWScript if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item)) { int removed = store.remove(*iter, toRemove, ptr); - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); + MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); + dropped.getCellRef().setOwner(""); toRemove -= removed; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 170c5fe94..befb8d82e 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -20,6 +20,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -416,8 +417,12 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - player.getClass().getNpcStats (player).setBounty(runtime[0].mFloat); + int bounty = runtime[0].mFloat; runtime.pop(); + player.getClass().getNpcStats (player).setBounty(bounty); + + if (bounty == 0) + MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index a2998ad03..2002bded7 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -112,7 +112,7 @@ bool FFmpeg_Decoder::getAVAudioData() if(!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, (*mStream)->codec->channels, + if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), mFrame->nb_samples, mOutputSampleFormat, 0) < 0) break; else @@ -147,8 +147,8 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) if(!getAVAudioData()) break; mFramePos = 0; - mFrameSize = mFrame->nb_samples * (*mStream)->codec->channels * - av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + mFrameSize = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * + av_get_bytes_per_sample(mOutputSampleFormat); } /* Get the amount of bytes remaining to be written, and clamp to @@ -285,47 +285,31 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * if(!mStream) fail("No audio stream info"); - if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8) - *type = SampleType_UInt8; - else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16) - *type = SampleType_Int16; - else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT) - *type = SampleType_Float32; + 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) - *type = SampleType_UInt8; + mOutputSampleFormat = AV_SAMPLE_FMT_U8; else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P) - *type = SampleType_Int16; - else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) - *type = SampleType_Float32; + mOutputSampleFormat = AV_SAMPLE_FMT_S16; else - fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name((*mStream)->codec->sample_fmt)); + mOutputSampleFormat = AV_SAMPLE_FMT_S16; + + if(mOutputSampleFormat == AV_SAMPLE_FMT_U8) + *type = SampleType_UInt8; + else if(mOutputSampleFormat == AV_SAMPLE_FMT_S16) + *type = SampleType_Int16; + else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT) + *type = SampleType_Float32; int64_t ch_layout = (*mStream)->codec->channel_layout; - if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_MONO) - *chans = ChannelConfig_Mono; - else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_STEREO) - *chans = ChannelConfig_Stereo; - else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_QUAD) - *chans = ChannelConfig_Quad; - else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) - *chans = ChannelConfig_5point1; - else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) - *chans = ChannelConfig_7point1; - else if((*mStream)->codec->channel_layout == 0) + if(ch_layout == 0) { /* Unknown channel layout. Try to guess. */ if((*mStream)->codec->channels == 1) - { - *chans = ChannelConfig_Mono; ch_layout = AV_CH_LAYOUT_MONO; - } else if((*mStream)->codec->channels == 2) - { - *chans = ChannelConfig_Stereo; ch_layout = AV_CH_LAYOUT_STEREO; - } else { std::stringstream sstr("Unsupported raw channel count: "); @@ -333,6 +317,25 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * fail(sstr.str()); } } + + 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; + + if(mOutputChannelLayout == AV_CH_LAYOUT_MONO) + *chans = ChannelConfig_Mono; + else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO) + *chans = ChannelConfig_Stereo; + else if(mOutputChannelLayout == AV_CH_LAYOUT_QUAD) + *chans = ChannelConfig_Quad; + else if(mOutputChannelLayout == AV_CH_LAYOUT_5POINT1) + *chans = ChannelConfig_5point1; + else if(mOutputChannelLayout == AV_CH_LAYOUT_7POINT1) + *chans = ChannelConfig_7point1; else { char str[1024]; @@ -343,17 +346,11 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * *samplerate = (*mStream)->codec->sample_rate; - 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 if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) - mOutputSampleFormat = AV_SAMPLE_FMT_FLT; - - if(mOutputSampleFormat != AV_SAMPLE_FMT_NONE) + if(mOutputSampleFormat != (*mStream)->codec->sample_fmt + || mOutputChannelLayout != ch_layout) { mSwr = swr_alloc_set_opts(mSwr, // SwrContext - ch_layout, // output ch layout + mOutputChannelLayout, // output ch layout mOutputSampleFormat, // output sample format (*mStream)->codec->sample_rate, // output sample rate ch_layout, // input ch layout @@ -383,8 +380,8 @@ void FFmpeg_Decoder::readAll(std::vector &output) while(getAVAudioData()) { - size_t got = mFrame->nb_samples * (*mStream)->codec->channels * - av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + size_t got = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * + av_get_bytes_per_sample(mOutputSampleFormat); const char *inbuf = reinterpret_cast(mFrameData[0]); output.insert(output.end(), inbuf, inbuf+got); } @@ -402,8 +399,8 @@ void FFmpeg_Decoder::rewind() size_t FFmpeg_Decoder::getSampleOffset() { - int delay = (mFrameSize-mFramePos) / (*mStream)->codec->channels / - av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + int delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / + av_get_bytes_per_sample(mOutputSampleFormat); return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay; } @@ -416,6 +413,7 @@ FFmpeg_Decoder::FFmpeg_Decoder() , mNextPts(0.0) , mSwr(0) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) + , mOutputChannelLayout(0) , mDataBuf(NULL) , mFrameData(NULL) , mDataBufLen(0) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 796f17350..2cdbbf363 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -59,6 +59,7 @@ namespace MWSound SwrContext *mSwr; enum AVSampleFormat mOutputSampleFormat; + int64_t mOutputChannelLayout; uint8_t *mDataBuf; uint8_t **mFrameData; int mDataBufLen; diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index bc7bb8023..468f8c82c 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -44,8 +44,8 @@ namespace MWSound size_t getSampleOffset() { - ssize_t clock_delay = (mFrameSize-mFramePos) / mAVStream->codec->channels / - av_get_bytes_per_sample(mAVStream->codec->sample_fmt); + ssize_t clock_delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / + av_get_bytes_per_sample(mOutputSampleFormat); return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay; } @@ -64,20 +64,20 @@ namespace MWSound virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) { - if (sampleFormat == AV_SAMPLE_FMT_U8P) + if (sampleFormat == AV_SAMPLE_FMT_U8P || sampleFormat == AV_SAMPLE_FMT_U8) sampleFormat = AV_SAMPLE_FMT_U8; - else if (sampleFormat == AV_SAMPLE_FMT_S16P) + else if (sampleFormat == AV_SAMPLE_FMT_S16P || sampleFormat == AV_SAMPLE_FMT_S16) sampleFormat = AV_SAMPLE_FMT_S16; - else if (sampleFormat == AV_SAMPLE_FMT_FLTP) - sampleFormat = AV_SAMPLE_FMT_FLT; + else if (sampleFormat == AV_SAMPLE_FMT_FLTP || sampleFormat == AV_SAMPLE_FMT_FLT) + sampleFormat = AV_SAMPLE_FMT_S16; // FIXME: check for AL_EXT_FLOAT32 support else - sampleFormat = AV_SAMPLE_FMT_FLT; + sampleFormat = AV_SAMPLE_FMT_S16; - if (channelLayout != AV_CH_LAYOUT_MONO - && channelLayout != AV_CH_LAYOUT_5POINT1 - && channelLayout != AV_CH_LAYOUT_7POINT1 - && channelLayout != AV_CH_LAYOUT_STEREO - && channelLayout != AV_CH_LAYOUT_QUAD) + if (channelLayout == AV_CH_LAYOUT_5POINT1 || channelLayout == AV_CH_LAYOUT_7POINT1 + || channelLayout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support + channelLayout = AV_CH_LAYOUT_STEREO; + else if (channelLayout != AV_CH_LAYOUT_MONO + && channelLayout != AV_CH_LAYOUT_STEREO) channelLayout = AV_CH_LAYOUT_STEREO; } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index ba6fad7ba..e322ef4a4 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -149,6 +149,17 @@ namespace MWWorld forEachImp (functor, mCreatureLists); } + template + bool forEachContainer (Functor& functor) + { + mHasState = true; + + return + forEachImp (functor, mContainers) && + forEachImp (functor, mCreatures) && + forEachImp (functor, mNpcs); + } + bool isExterior() const; Ptr searchInContainer (const std::string& id); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 085b079d9..45728371b 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -272,7 +272,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.mCell = actorPtr.getCell(); } - item.mContainerStore = 0; + item.mContainerStore = this; MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d8e0d52b8..5d07a26a0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -657,6 +657,47 @@ namespace MWWorld return mWorldScene->searchPtrViaActorId (actorId); } + struct FindContainerFunctor + { + Ptr mContainedPtr; + Ptr mResult; + + FindContainerFunctor(const Ptr& containedPtr) : mContainedPtr(containedPtr) {} + + bool operator() (Ptr ptr) + { + if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr)) + { + mResult = ptr; + return false; + } + + return true; + } + }; + + Ptr World::findContainer(const Ptr& ptr) + { + if (ptr.isInCell()) + return Ptr(); + + Ptr player = getPlayerPtr(); + if (ptr.getContainerStore() == &player.getClass().getContainerStore(player)) + return player; + + const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) + { + FindContainerFunctor functor(ptr); + (*cellIt)->forEachContainer(functor); + + if (!functor.mResult.isEmpty()) + return functor.mResult; + } + + return Ptr(); + } + void World::addContainerScripts(const Ptr& reference, CellStore * cell) { if( reference.getTypeName()==typeid (ESM::Container).name() || diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 334e799f2..fef279705 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -260,6 +260,10 @@ namespace MWWorld virtual Ptr searchPtrViaActorId (int actorId); ///< Search is limited to the active cells. + virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr); + ///< Return a pointer to a liveCellRef which contains \a ptr. + /// \note Search is limited to the active cells. + virtual void adjustPosition (const Ptr& ptr, bool force); ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake index 51589698f..f2acf9d33 100644 --- a/cmake/FindOGRE.cmake +++ b/cmake/FindOGRE.cmake @@ -382,7 +382,6 @@ endmacro() ogre_find_component(Paging OgrePaging.h) # look for Overlay component ogre_find_component(Overlay OgreOverlaySystem.h) -ogre_find_component(Overlay OgreOverlay.h) # look for Terrain component ogre_find_component(Terrain OgreTerrain.h) # look for Property component diff --git a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt index f34ffa64b..299a57799 100644 --- a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt +++ b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt @@ -3,15 +3,14 @@ set(OGRE_FFMPEG_VIDEOPLAYER_LIBRARY "ogre-ffmpeg-videoplayer") # Sources set(OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES - videoplayer.cpp - videostate.cpp - videodefs.hpp + videoplayer.cpp + videostate.cpp + videodefs.hpp libavwrapper.cpp audiodecoder.cpp audiofactory.hpp ) - # Find FFMPEG set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE AVRESAMPLE) unset(FFMPEG_LIBRARIES CACHE) @@ -30,10 +29,14 @@ else() message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).") endif() endif() - include_directories(${FFMPEG_INCLUDE_DIRS}) +# Find Boost +set(BOOST_COMPONENTS thread) +find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +include_directories(${Boost_INCLUDE_DIRS}) + add_library(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} STATIC ${OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES}) -target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${VIDEO_FFMPEG_LIBRARIES}) +target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${VIDEO_FFMPEG_LIBRARIES} ${Boost_LIBRARIES}) link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp b/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp index 54fe2b24f..41313d5d5 100644 --- a/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp +++ b/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp @@ -152,8 +152,8 @@ int MovieAudioDecoder::synchronize_audio() double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef); if(fabs(avg_diff) >= mAudioDiffThreshold) { - int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * - mAVStream->codec->channels; + int n = av_get_bytes_per_sample(mOutputSampleFormat) * + av_get_channel_layout_nb_channels(mOutputChannelLayout); sample_skip = ((int)(diff * mAVStream->codec->sample_rate) * n); } } @@ -161,7 +161,7 @@ int MovieAudioDecoder::synchronize_audio() return sample_skip; } -int MovieAudioDecoder::audio_decode_frame(AVFrame *frame) +int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) { AVPacket *pkt = &mPacket; @@ -191,7 +191,7 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame) if(!mDataBuf || mDataBufLen < frame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, mAVStream->codec->channels, + if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), frame->nb_samples, mOutputSampleFormat, 0) < 0) break; else @@ -212,8 +212,8 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame) (double)mAVStream->codec->sample_rate; /* We have data, return it and come back for more later */ - return frame->nb_samples * mAVStream->codec->channels * - av_get_bytes_per_sample(mAVStream->codec->sample_fmt); + return frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * + av_get_bytes_per_sample(mOutputSampleFormat); } av_free_packet(pkt); @@ -221,6 +221,18 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame) if(mVideoState->audioq.get(pkt, mVideoState) < 0) return -1; + if(pkt->data == mVideoState->mFlushPktData) + { + avcodec_flush_buffers(mAVStream->codec); + mAudioDiffAccum = 0.0; + mAudioDiffAvgCount = 0; + mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts; + sample_skip = 0; + + if(mVideoState->audioq.get(pkt, mVideoState) < 0) + return -1; + } + /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts; @@ -229,6 +241,16 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame) size_t MovieAudioDecoder::read(char *stream, size_t len) { + if (mVideoState->mPaused) + { + // fill the buffer with silence + size_t sampleSize = av_get_bytes_per_sample(mOutputSampleFormat); + char* data[1]; + data[0] = stream; + av_samples_set_silence((uint8_t**)data, 0, len/sampleSize, 1, mOutputSampleFormat); + return len; + } + int sample_skip = synchronize_audio(); size_t total = 0; @@ -237,7 +259,7 @@ size_t MovieAudioDecoder::read(char *stream, size_t len) if(mFramePos >= mFrameSize) { /* We have already sent all our data; get more */ - mFrameSize = audio_decode_frame(mFrame); + mFrameSize = audio_decode_frame(mFrame, sample_skip); if(mFrameSize < 0) { /* If error, we're done */ @@ -260,8 +282,8 @@ size_t MovieAudioDecoder::read(char *stream, size_t len) { len1 = std::min(len1, -mFramePos); - int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * - mAVStream->codec->channels; + int n = av_get_bytes_per_sample(mOutputSampleFormat) + * av_get_channel_layout_nb_channels(mOutputChannelLayout); /* add samples by copying the first sample*/ if(n == 1) diff --git a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp index 88406d51d..b05b16d42 100644 --- a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp @@ -77,7 +77,8 @@ private: * skip (negative means to duplicate). */ int synchronize_audio(); - int audio_decode_frame(AVFrame *frame); + /// @param sample_skip If seeking happened, the sample_skip variable will be reset to 0. + int audio_decode_frame(AVFrame *frame, int &sample_skip); public: MovieAudioDecoder(VideoState *is); @@ -101,6 +102,8 @@ public: virtual double getAudioClock(); /// This is the main interface to be used by the user's audio library. + /// @par Request filling the \a stream with \a len number of bytes. + /// @return The number of bytes read (may not be the requested number if we arrived at the end of the audio stream) size_t read(char *stream, size_t len); }; diff --git a/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp b/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp index cf1ff46a8..06abd6a74 100644 --- a/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp +++ b/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp @@ -12,6 +12,7 @@ class MovieAudioFactory { public: virtual boost::shared_ptr createDecoder(VideoState* videoState) = 0; + virtual ~MovieAudioFactory() {} }; } diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp index 434b676ee..c2daf3579 100644 --- a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp +++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp @@ -1,5 +1,6 @@ #include "videoplayer.hpp" +#include "audiofactory.hpp" #include "videostate.hpp" namespace Video @@ -31,6 +32,13 @@ void VideoPlayer::playVideo(const std::string &resourceName) mState = new VideoState; mState->setAudioFactory(mAudioFactory.get()); mState->init(resourceName); + + // wait until we have the first picture + while (mState->video_st && mState->mTexture.isNull()) + { + if (!mState->update()) + break; + } } catch(std::exception& e) { std::cerr<< "Failed to play video: "<update()) - close(); - } + return mState->update(); + return false; } std::string VideoPlayer::getTextureName() { std::string name; - if (mState) + if (mState && !mState->mTexture.isNull()) name = mState->mTexture->getName(); return name; } @@ -58,7 +64,7 @@ std::string VideoPlayer::getTextureName() int VideoPlayer::getVideoWidth() { int width=0; - if (mState) + if (mState && !mState->mTexture.isNull()) width = mState->mTexture->getWidth(); return width; } @@ -66,7 +72,7 @@ int VideoPlayer::getVideoWidth() int VideoPlayer::getVideoHeight() { int height=0; - if (mState) + if (mState && !mState->mTexture.isNull()) height = mState->mTexture->getHeight(); return height; } @@ -82,14 +88,48 @@ void VideoPlayer::close() } } -bool VideoPlayer::isPlaying () -{ - return mState != NULL; -} - bool VideoPlayer::hasAudioStream() { return mState && mState->audio_st != NULL; } +void VideoPlayer::play() +{ + if (mState) + mState->setPaused(false); +} + +void VideoPlayer::pause() +{ + if (mState) + mState->setPaused(true); +} + +bool VideoPlayer::isPaused() +{ + if (mState) + return mState->mPaused; + return true; +} + +double VideoPlayer::getCurrentTime() +{ + if (mState) + return mState->get_master_clock(); + return 0.0; +} + +void VideoPlayer::seek(double time) +{ + if (mState) + mState->seekTo(time); +} + +double VideoPlayer::getDuration() +{ + if (mState) + return mState->getDuration(); + return 0.0; +} + } diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp index 750ad02e5..2727ac6f0 100644 --- a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp +++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp @@ -29,16 +29,29 @@ namespace Video bool hasAudioStream(); /// Play the given video. If a video is already playing, the old video is closed first. + /// @note The video will be unpaused by default. Use the pause() and play() methods to control pausing. void playVideo (const std::string& resourceName); + /// Get the current playback time position in the video, in seconds + double getCurrentTime(); + + /// Get the duration of the video in seconds + double getDuration(); + + /// Seek to the specified time position in the video + void seek(double time); + + void play(); + void pause(); + bool isPaused(); + /// This should be called every frame by the user to update the video texture. - void update(); + /// @return Returns true if the video is still playing, false if we have reached the end of the video stream. + bool update(); /// Stop the currently playing video, if a video is playing. void close(); - bool isPlaying(); - /// Return the texture name of the currently playing video, or "" if no video is playing. std::string getTextureName(); /// Return the width of the currently playing video, or 0 if no video is playing. diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.cpp b/extern/ogre-ffmpeg-videoplayer/videostate.cpp index c77723421..b36543880 100644 --- a/extern/ogre-ffmpeg-videoplayer/videostate.cpp +++ b/extern/ogre-ffmpeg-videoplayer/videostate.cpp @@ -25,12 +25,26 @@ extern "C" #include #endif + #include + #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) #define av_frame_alloc avcodec_alloc_frame #endif } +static const char* flushString = "FLUSH"; +struct FlushPacket : AVPacket +{ + FlushPacket() + : AVPacket() + { + data = ( (uint8_t*)flushString); + } +}; + +static FlushPacket flush_pkt; + #include "videoplayer.hpp" #include "audiodecoder.hpp" #include "audiofactory.hpp" @@ -46,14 +60,18 @@ namespace Video VideoState::VideoState() : format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT) - , external_clock_base(0.0) , audio_st(NULL) , video_st(NULL), frame_last_pts(0.0) , video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0) , pictq_rindex(0), pictq_windex(0) - , quit(false) + , mQuit(false), mPaused(false) , mAudioFactory(NULL) + , mSeekRequested(false) + , mSeekPos(0) + , mVideoEnded(false) { + mFlushPktData = flush_pkt.data; + // Register all formats and codecs av_register_all(); } @@ -77,7 +95,7 @@ void PacketQueue::put(AVPacket *pkt) pkt1->pkt = *pkt; pkt1->next = NULL; - if(pkt1->pkt.destruct == NULL) + if(pkt->data != flush_pkt.data && pkt1->pkt.destruct == NULL) { if(av_dup_packet(&pkt1->pkt) < 0) { @@ -104,7 +122,7 @@ void PacketQueue::put(AVPacket *pkt) int PacketQueue::get(AVPacket *pkt, VideoState *is) { boost::unique_lock lock(this->mutex); - while(!is->quit) + while(!is->mQuit) { AVPacketList *pkt1 = this->first_pkt; if(pkt1) @@ -143,7 +161,8 @@ void PacketQueue::clear() for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; - av_free_packet(&pkt->pkt); + if (pkt->pkt.data != flush_pkt.data) + av_free_packet(&pkt->pkt); av_freep(&pkt); } this->last_pkt = NULL; @@ -188,14 +207,17 @@ void VideoState::video_display(VideoPicture *vp) { if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) { - - if(static_cast(mTexture->getWidth()) != (*this->video_st)->codec->width || - static_cast(mTexture->getHeight()) != (*this->video_st)->codec->height) + if (mTexture.isNull()) { - mTexture->unload(); - mTexture->setWidth((*this->video_st)->codec->width); - mTexture->setHeight((*this->video_st)->codec->height); - mTexture->createInternalResources(); + static int i = 0; + mTexture = Ogre::TextureManager::getSingleton().createManual( + "ffmpeg/VideoTexture" + Ogre::StringConverter::toString(++i), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + (*this->video_st)->codec->width, (*this->video_st)->codec->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); } Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); Ogre::HardwarePixelBufferSharedPtr buffer = mTexture->getBuffer(); @@ -205,6 +227,7 @@ void VideoState::video_display(VideoPicture *vp) void VideoState::video_refresh() { + boost::mutex::scoped_lock lock(this->pictq_mutex); if(this->pictq_size == 0) return; @@ -212,16 +235,15 @@ void VideoState::video_refresh() { VideoPicture* vp = &this->pictq[this->pictq_rindex]; this->video_display(vp); + this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->frame_last_pts = vp->pts; - this->pictq_mutex.lock(); this->pictq_size--; this->pictq_cond.notify_one(); - this->pictq_mutex.unlock(); } else { - const float threshold = 0.03; + const float threshold = 0.03f; if (this->pictq[pictq_rindex].pts > this->get_master_clock() + threshold) return; // not ready yet to show this picture @@ -236,19 +258,18 @@ void VideoState::video_refresh() break; } + assert (this->pictq_rindex < VIDEO_PICTURE_QUEUE_SIZE); VideoPicture* vp = &this->pictq[this->pictq_rindex]; this->video_display(vp); this->frame_last_pts = vp->pts; - this->pictq_mutex.lock(); this->pictq_size -= i; // update queue for next picture this->pictq_size--; - this->pictq_rindex++; + this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_cond.notify_one(); - this->pictq_mutex.unlock(); } } @@ -260,12 +281,14 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) /* wait until we have a new pic */ { boost::unique_lock lock(this->pictq_mutex); - while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit) + while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->mQuit) this->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); } - if(this->quit) + if(this->mQuit) return -1; + this->pictq_mutex.lock(); + // windex is set to 0 initially vp = &this->pictq[this->pictq_windex]; @@ -292,7 +315,6 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; - this->pictq_mutex.lock(); this->pictq_size++; this->pictq_mutex.unlock(); @@ -353,6 +375,21 @@ void VideoState::video_thread_loop(VideoState *self) while(self->videoq.get(packet, self) >= 0) { + if(packet->data == flush_pkt.data) + { + avcodec_flush_buffers((*self->video_st)->codec); + + self->pictq_mutex.lock(); + self->pictq_size = 0; + self->pictq_rindex = 0; + self->pictq_windex = 0; + self->pictq_mutex.unlock(); + + self->frame_last_pts = packet->pts * av_q2d((*self->video_st)->time_base); + global_video_pkt_pts = self->frame_last_pts; + continue; + } + // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame @@ -394,8 +431,67 @@ void VideoState::decode_thread_loop(VideoState *self) throw std::runtime_error("No streams to decode"); // main decode loop - while(!self->quit) + while(!self->mQuit) { + if(self->mSeekRequested) + { + uint64_t seek_target = self->mSeekPos; + int streamIndex = -1; + + int videoStreamIndex = -1;; + int audioStreamIndex = -1; + if (self->video_st) + videoStreamIndex = self->video_st - self->format_ctx->streams; + if (self->audio_st) + audioStreamIndex = self->audio_st - self->format_ctx->streams; + + if(videoStreamIndex >= 0) + streamIndex = videoStreamIndex; + else if(audioStreamIndex >= 0) + streamIndex = audioStreamIndex; + + uint64_t timestamp = seek_target; + + // QtCreator's highlighter doesn't like AV_TIME_BASE_Q's {} initializer for some reason + AVRational avTimeBaseQ = AVRational(); // = AV_TIME_BASE_Q; + avTimeBaseQ.num = 1; + avTimeBaseQ.den = AV_TIME_BASE; + + if(streamIndex >= 0) + timestamp = av_rescale_q(seek_target, avTimeBaseQ, self->format_ctx->streams[streamIndex]->time_base); + + // AVSEEK_FLAG_BACKWARD appears to be needed, otherwise ffmpeg may seek to a keyframe *after* the given time + // we want to seek to any keyframe *before* the given time, so we can continue decoding as normal from there on + if(av_seek_frame(self->format_ctx, streamIndex, timestamp, AVSEEK_FLAG_BACKWARD) < 0) + std::cerr << "Error seeking " << self->format_ctx->filename << std::endl; + else + { + // Clear the packet queues and put a special packet with the new clock time + if(audioStreamIndex >= 0) + { + self->audioq.clear(); + flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ, + self->format_ctx->streams[audioStreamIndex]->time_base); + self->audioq.put(&flush_pkt); + } + if(videoStreamIndex >= 0) + { + self->videoq.clear(); + flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ, + self->format_ctx->streams[videoStreamIndex]->time_base); + self->videoq.put(&flush_pkt); + } + self->pictq_mutex.lock(); + self->pictq_size = 0; + self->pictq_rindex = 0; + self->pictq_windex = 0; + self->pictq_mutex.unlock(); + self->mExternalClock.set(seek_target); + } + self->mSeekRequested = false; + } + + if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { @@ -404,7 +500,13 @@ void VideoState::decode_thread_loop(VideoState *self) } if(av_read_frame(pFormatCtx, packet) < 0) - break; + { + if (self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0) + self->mVideoEnded = true; + continue; + } + else + self->mVideoEnded = false; // Is this a packet from the video stream? if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams) @@ -414,17 +516,6 @@ void VideoState::decode_thread_loop(VideoState *self) else av_free_packet(packet); } - - /* all done - wait for it */ - self->videoq.flush(); - self->audioq.flush(); - while(!self->quit) - { - // EOF reached, all packets processed, we can exit now - if(self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } } catch(std::runtime_error& e) { std::cerr << "An error occured playing the video: " << e.what () << std::endl; @@ -433,17 +524,14 @@ void VideoState::decode_thread_loop(VideoState *self) std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; } - self->quit = true; + self->mQuit = true; } bool VideoState::update() { - if(this->quit) - return false; - this->video_refresh(); - return true; + return !this->mVideoEnded; } @@ -510,7 +598,7 @@ void VideoState::init(const std::string& resourceName) unsigned int i; this->av_sync_type = AV_SYNC_DEFAULT; - this->quit = false; + this->mQuit = false; this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); if(this->stream.isNull()) @@ -564,7 +652,7 @@ void VideoState::init(const std::string& resourceName) audio_index = i; } - this->external_clock_base = av_gettime(); + mExternalClock.set(0); if(audio_index >= 0) this->stream_open(audio_index, this->format_ctx); @@ -572,24 +660,6 @@ void VideoState::init(const std::string& resourceName) if(video_index >= 0) { this->stream_open(video_index, this->format_ctx); - - int width = (*this->video_st)->codec->width; - int height = (*this->video_st)->codec->height; - static int i = 0; - this->mTexture = Ogre::TextureManager::getSingleton().createManual( - "ffmpeg/VideoTexture" + Ogre::StringConverter::toString(++i), - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - width, height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - - // initialize to (0,0,0,0) - std::vector buffer; - buffer.resize(width * height, 0); - Ogre::PixelBox pb(width, height, 1, Ogre::PF_BYTE_RGBA, &buffer[0]); - this->mTexture->getBuffer()->blitFromMemory(pb); } @@ -598,13 +668,13 @@ void VideoState::init(const std::string& resourceName) void VideoState::deinit() { - this->quit = true; + this->mQuit = true; + + this->audioq.flush(); + this->videoq.flush(); mAudioDecoder.reset(); - this->audioq.cond.notify_one(); - this->videoq.cond.notify_one(); - if (this->parse_thread.joinable()) this->parse_thread.join(); if (this->video_thread.joinable()) @@ -639,11 +709,17 @@ void VideoState::deinit() } avformat_close_input(&this->format_ctx); } + + if (!mTexture.isNull()) + { + Ogre::TextureManager::getSingleton().remove(mTexture->getName()); + mTexture.setNull(); + } } double VideoState::get_external_clock() { - return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0; + return mExternalClock.get() / 1000000.0; } double VideoState::get_master_clock() @@ -667,5 +743,62 @@ double VideoState::get_audio_clock() return mAudioDecoder->getAudioClock(); } +void VideoState::setPaused(bool isPaused) +{ + this->mPaused = isPaused; + mExternalClock.setPaused(isPaused); +} + +void VideoState::seekTo(double time) +{ + time = std::max(0.0, time); + time = std::min(getDuration(), time); + mSeekPos = (uint64_t) (time * AV_TIME_BASE); + mSeekRequested = true; +} + +double VideoState::getDuration() +{ + return this->format_ctx->duration / 1000000.0; +} + + +ExternalClock::ExternalClock() + : mTimeBase(av_gettime()) + , mPausedAt(0) + , mPaused(false) +{ +} + +void ExternalClock::setPaused(bool paused) +{ + boost::mutex::scoped_lock lock(mMutex); + if (mPaused == paused) + return; + if (paused) + { + mPausedAt = av_gettime() - mTimeBase; + } + else + mTimeBase = av_gettime() - mPausedAt; + mPaused = paused; +} + +uint64_t ExternalClock::get() +{ + boost::mutex::scoped_lock lock(mMutex); + if (mPaused) + return mPausedAt; + else + return av_gettime() - mTimeBase; +} + +void ExternalClock::set(uint64_t time) +{ + boost::mutex::scoped_lock lock(mMutex); + mTimeBase = av_gettime() - time; + mPausedAt = time; +} + } diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.hpp b/extern/ogre-ffmpeg-videoplayer/videostate.hpp index 90ebec0a3..cdeb2d0e3 100644 --- a/extern/ogre-ffmpeg-videoplayer/videostate.hpp +++ b/extern/ogre-ffmpeg-videoplayer/videostate.hpp @@ -27,6 +27,21 @@ struct VideoState; class MovieAudioFactory; class MovieAudioDecoder; +struct ExternalClock +{ + ExternalClock(); + + uint64_t mTimeBase; + uint64_t mPausedAt; + bool mPaused; + + boost::mutex mMutex; + + void setPaused(bool paused); + uint64_t get(); + void set(uint64_t time); +}; + struct PacketQueue { PacketQueue() : first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0) @@ -66,6 +81,11 @@ struct VideoState { void init(const std::string& resourceName); void deinit(); + void setPaused(bool isPaused); + void seekTo(double time); + + double getDuration(); + int stream_open(int stream_index, AVFormatContext *pFormatCtx); bool update(); @@ -93,15 +113,18 @@ struct VideoState { MovieAudioFactory* mAudioFactory; boost::shared_ptr mAudioDecoder; + ExternalClock mExternalClock; + Ogre::DataStreamPtr stream; AVFormatContext* format_ctx; int av_sync_type; - uint64_t external_clock_base; AVStream** audio_st; PacketQueue audioq; + uint8_t* mFlushPktData; + AVStream** video_st; double frame_last_pts; double video_clock; /// shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); #endif @@ -514,11 +514,11 @@ #endif #if UNDERWATER - float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); + float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0.0,0.0,1.0), waterLevel); #endif #if SHADOWS || SHADOWS_PSSM - shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0)); + shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0.0)); #else shOutputColour(0) *= lightResult; #endif @@ -574,7 +574,7 @@ #endif // prevent negative colour output (for example with negative lights) - shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0.0,0.0,0.0)); } #endif diff --git a/files/materials/shadows.h b/files/materials/shadows.h index 65dffe492..eba3a3ea7 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -6,11 +6,11 @@ float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) shadowMapPos /= shadowMapPos.w; float3 o = float3(offset.xy, -offset.x) * 0.3; //float3 o = float3(0,0,0); - float c = (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left - c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right - c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left - c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right - return c / 4; + float c = (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1.0 : 0.0; // top left + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1.0 : 0.0; // bottom right + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1.0 : 0.0; // bottom left + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1.0 : 0.0; // top right + return c / 4.0; } diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 3b1cfea64..2336d5b2c 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -2,47 +2,51 @@ - + - + - + - - + + - - + + - + - + - + + + - + + + - - + + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index dee2f4fcf..1d071e621 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -3,7 +3,7 @@ - + diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index 6cdd4c02a..11119c404 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -69,33 +69,46 @@ - - - - - + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + +