diff --git a/AUTHORS.md b/AUTHORS.md index 60e969cdc..82372ae36 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -37,6 +37,7 @@ Programmers Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) darkf + devnexen Dieho Dmitry Shkurskiy (endorph) Douglas Diniz (Dgdiniz) @@ -75,6 +76,7 @@ Programmers Lars Söderberg (Lazaroth) lazydev Leon Saunders (emoose) + lohikaarme Lukasz Gromanowski (lgro) Manuel Edelmann (vorenon) Marc Bouvier (CramitDeFrog) diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index 8f090b3fc..5c2bcc402 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -1,3 +1,9 @@ +#include +#include +#include + +#include + #include "convertacdt.hpp" namespace ESSImport @@ -49,4 +55,49 @@ namespace ESSImport npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter; } + void convertANIS (const ANIS& anis, ESM::AnimationState& state) + { + static const char* animGroups[] = + { + "Idle", "Idle2", "Idle3", "Idle4", "Idle5", "Idle6", "Idle7", "Idle8", "Idle9", "Idlehh", "Idle1h", "Idle2c", + "Idle2w", "IdleSwim", "IdleSpell", "IdleCrossbow", "IdleSneak", "IdleStorm", "Torch", "Hit1", "Hit2", "Hit3", + "Hit4", "Hit5", "SwimHit1", "SwimHit2", "SwimHit3", "Death1", "Death2", "Death3", "Death4", "Death5", + "DeathKnockDown", "DeathKnockOut", "KnockDown", "KnockOut", "SwimDeath", "SwimDeath2", "SwimDeath3", + "SwimDeathKnockDown", "SwimDeathKnockOut", "SwimKnockOut", "SwimKnockDown", "SwimWalkForward", + "SwimWalkBack", "SwimWalkLeft", "SwimWalkRight", "SwimRunForward", "SwimRunBack", "SwimRunLeft", + "SwimRunRight", "SwimTurnLeft", "SwimTurnRight", "WalkForward", "WalkBack", "WalkLeft", "WalkRight", + "TurnLeft", "TurnRight", "RunForward", "RunBack", "RunLeft", "RunRight", "SneakForward", "SneakBack", + "SneakLeft", "SneakRight", "Jump", "WalkForwardhh", "WalkBackhh", "WalkLefthh", "WalkRighthh", + "TurnLefthh", "TurnRighthh", "RunForwardhh", "RunBackhh", "RunLefthh", "RunRighthh", "SneakForwardhh", + "SneakBackhh", "SneakLefthh", "SneakRighthh", "Jumphh", "WalkForward1h", "WalkBack1h", "WalkLeft1h", + "WalkRight1h", "TurnLeft1h", "TurnRight1h", "RunForward1h", "RunBack1h", "RunLeft1h", "RunRight1h", + "SneakForward1h", "SneakBack1h", "SneakLeft1h", "SneakRight1h", "Jump1h", "WalkForward2c", "WalkBack2c", + "WalkLeft2c", "WalkRight2c", "TurnLeft2c", "TurnRight2c", "RunForward2c", "RunBack2c", "RunLeft2c", + "RunRight2c", "SneakForward2c", "SneakBack2c", "SneakLeft2c", "SneakRight2c", "Jump2c", "WalkForward2w", + "WalkBack2w", "WalkLeft2w", "WalkRight2w", "TurnLeft2w", "TurnRight2w", "RunForward2w", "RunBack2w", + "RunLeft2w", "RunRight2w", "SneakForward2w", "SneakBack2w", "SneakLeft2w", "SneakRight2w", "Jump2w", + "SpellCast", "SpellTurnLeft", "SpellTurnRight", "Attack1", "Attack2", "Attack3", "SwimAttack1", + "SwimAttack2", "SwimAttack3", "HandToHand", "Crossbow", "BowAndArrow", "ThrowWeapon", "WeaponOneHand", + "WeaponTwoHand", "WeaponTwoWide", "Shield", "PickProbe", "InventoryHandToHand", "InventoryWeaponOneHand", + "InventoryWeaponTwoHand", "InventoryWeaponTwoWide" + }; + + if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups))) + { + std::string group(animGroups[anis.mGroupIndex]); + Misc::StringUtils::lowerCaseInPlace(group); + + ESM::AnimationState::ScriptedAnimation scriptedAnim; + scriptedAnim.mGroup = group; + scriptedAnim.mTime = anis.mTime; + scriptedAnim.mAbsolute = true; + // Neither loop count nor queueing seems to be supported by the ess format. + scriptedAnim.mLoopCount = std::numeric_limits::max(); + state.mScriptedAnims.push_back(scriptedAnim); + } + else + // TODO: Handle 0xFF index, which seems to be used for finished animations. + std::cerr << "unknown animation group index: " << static_cast(anis.mGroupIndex) << std::endl; + } + } diff --git a/apps/essimporter/convertacdt.hpp b/apps/essimporter/convertacdt.hpp index bc9a7bd00..4059dd1af 100644 --- a/apps/essimporter/convertacdt.hpp +++ b/apps/essimporter/convertacdt.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "importacdt.hpp" @@ -18,6 +19,8 @@ namespace ESSImport void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats); void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); + + void convertANIS (const ANIS& anis, ESM::AnimationState& state); } #endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index afd0ef131..a428a8c71 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -34,6 +34,9 @@ namespace objstate.mCount = 0; convertSCRI(cellref.mSCRI, objstate.mLocals); objstate.mHasLocals = !objstate.mLocals.mVariables.empty(); + + if (cellref.mHasANIS) + convertANIS(cellref.mANIS, objstate.mAnimationState); } bool isIndexedRefId(const std::string& indexedRefId) diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index fb0dccaf5..0ddd2eb64 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -123,8 +123,13 @@ namespace ESSImport if (esm.isNextSub("ND3D")) esm.skipHSub(); + + mHasANIS = false; if (esm.isNextSub("ANIS")) - esm.skipHSub(); + { + mHasANIS = true; + esm.getHT(mANIS); + } } } diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index eacb2edf1..bf48d1f78 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -55,6 +55,12 @@ namespace ESSImport unsigned char mCorpseClearCountdown; // hours? unsigned char mUnknown3[71]; }; + struct ANIS + { + unsigned char mGroupIndex; + unsigned char mUnknown[3]; + float mTime; + }; #pragma pack(pop) struct ActorData : public ESM::CellRef @@ -77,6 +83,9 @@ namespace ESSImport SCRI mSCRI; + bool mHasANIS; + ANIS mANIS; // scripted animation state + void load(ESM::ESMReader& esm); }; diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6e19c03b2..8a830f2cb 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -87,6 +87,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller + cellwater ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/world/cellcoordinates.hpp b/apps/opencs/model/world/cellcoordinates.hpp index f8851a6d9..696868d00 100644 --- a/apps/opencs/model/world/cellcoordinates.hpp +++ b/apps/opencs/model/world/cellcoordinates.hpp @@ -37,7 +37,8 @@ namespace CSMWorld /// /// \note The worldspace part of \a id is ignored static std::pair fromId (const std::string& id); - + + /// \return cell coordinates such that given world coordinates are in it. static std::pair coordinatesToCellIndex (float x, float y); }; diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index dc22fd511..395fbf95f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -16,6 +16,7 @@ #include "../../model/world/refcollection.hpp" #include "../../model/world/cellcoordinates.hpp" +#include "cellwater.hpp" #include "mask.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" @@ -111,6 +112,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st } mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); + mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); } } diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index a5b581d24..8f68e9f53 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -35,6 +35,7 @@ namespace CSMWorld namespace CSVRender { + class CellWater; class Pathgrid; class TagBase; @@ -49,6 +50,7 @@ namespace CSVRender std::auto_ptr mCellArrows[4]; std::auto_ptr mCellMarker; std::auto_ptr mCellBorder; + std::auto_ptr mCellWater; std::auto_ptr mPathgrid; bool mDeleted; int mSubMode; diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index e0d270f85..abc337ce2 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -75,6 +75,7 @@ CSVRender::CellMarker::CellMarker( mMarkerNode->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN); mMarkerNode->setAutoScaleToScreen(true); mMarkerNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + mMarkerNode->getOrCreateStateSet()->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN + 1, "RenderBin"); mMarkerNode->setUserData(new CellMarkerTag(this)); mMarkerNode->setNodeMask(Mask_CellMarker); diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp new file mode 100644 index 000000000..b8975da49 --- /dev/null +++ b/apps/opencs/view/render/cellwater.cpp @@ -0,0 +1,177 @@ +#include "cellwater.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/cell.hpp" +#include "../../model/world/cellcoordinates.hpp" +#include "../../model/world/data.hpp" + +#include "mask.hpp" + +namespace CSVRender +{ + const int CellWater::CellSize = ESM::Land::REAL_SIZE; + + CellWater::CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, + const CSMWorld::CellCoordinates& cellCoords) + : mData(data) + , mId(id) + , mParentNode(cellNode) + , mWaterTransform(0) + , mWaterNode(0) + , mWaterGeometry(0) + , mDeleted(false) + , mExterior(false) + , mHasWater(false) + { + mWaterTransform = new osg::PositionAttitudeTransform(); + mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f, + cellCoords.getY() * CellSize + CellSize / 2.f, 0)); + + mWaterTransform->setNodeMask(Mask_Water); + mParentNode->addChild(mWaterTransform); + + mWaterNode = new osg::Geode(); + mWaterTransform->addChild(mWaterNode); + + int cellIndex = mData.getCells().searchId(mId); + if (cellIndex > -1) + { + updateCellData(mData.getCells().getRecord(cellIndex)); + } + + // Keep water existance/height up to date + QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells); + connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&))); + } + + CellWater::~CellWater() + { + mParentNode->removeChild(mWaterTransform); + } + + void CellWater::updateCellData(const CSMWorld::Record& cellRecord) + { + mDeleted = cellRecord.isDeleted(); + if (!mDeleted) + { + const CSMWorld::Cell& cell = cellRecord.get(); + + if (mExterior != cell.isExterior() || mHasWater != cell.hasWater()) + { + mExterior = cellRecord.get().isExterior(); + mHasWater = cellRecord.get().hasWater(); + + recreate(); + } + + float waterHeight = -1; + if (!mExterior) + { + waterHeight = cellRecord.get().mWater; + } + + osg::Vec3d pos = mWaterTransform->getPosition(); + pos.z() = waterHeight; + mWaterTransform->setPosition(pos); + } + else + { + recreate(); + } + } + + void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) + { + const CSMWorld::Collection& cells = mData.getCells(); + + int rowStart = -1; + int rowEnd = -1; + + if (topLeft.parent().isValid()) + { + rowStart = topLeft.parent().row(); + rowEnd = bottomRight.parent().row(); + } + else + { + rowStart = topLeft.row(); + rowEnd = bottomRight.row(); + } + + for (int row = rowStart; row <= rowEnd; ++row) + { + const CSMWorld::Record& cellRecord = cells.getRecord(row); + + if (Misc::StringUtils::lowerCase(cellRecord.get().mId) == mId) + updateCellData(cellRecord); + } + } + + void CellWater::recreate() + { + const int InteriorScalar = 20; + const int SegmentsPerCell = 1; + const int TextureRepeatsPerCell = 6; + + const float Alpha = 0.5f; + + const int RenderBin = osg::StateSet::TRANSPARENT_BIN - 1; + + if (mWaterGeometry) + { + mWaterNode->removeDrawable(mWaterGeometry); + mWaterGeometry = 0; + } + + if (mDeleted || !mHasWater) + return; + + float size; + int segments; + float textureRepeats; + + if (mExterior) + { + size = CellSize; + segments = SegmentsPerCell; + textureRepeats = TextureRepeatsPerCell; + } + else + { + size = CellSize * InteriorScalar; + segments = SegmentsPerCell * InteriorScalar; + textureRepeats = TextureRepeatsPerCell * InteriorScalar; + } + + mWaterGeometry = SceneUtil::createWaterGeometry(size, segments, textureRepeats); + mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin)); + + // Add water texture + std::string textureName = mData.getFallbackMap()->getFallbackString("Water_SurfaceTexture"); + textureName = "textures/water/" + textureName + "00.dds"; + + Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager(); + + osg::ref_ptr waterTexture = new osg::Texture2D(); + waterTexture->setImage(imageManager->getImage(textureName)); + waterTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + waterTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + + mWaterGeometry->getStateSet()->setTextureAttributeAndModes(0, waterTexture, osg::StateAttribute::ON); + + + mWaterNode->addDrawable(mWaterGeometry); + } +} diff --git a/apps/opencs/view/render/cellwater.hpp b/apps/opencs/view/render/cellwater.hpp new file mode 100644 index 000000000..d2ed9b458 --- /dev/null +++ b/apps/opencs/view/render/cellwater.hpp @@ -0,0 +1,70 @@ +#ifndef CSV_RENDER_CELLWATER_H +#define CSV_RENDER_CELLWATER_H + +#include + +#include + +#include +#include + +#include "../../model/world/record.hpp" + +namespace osg +{ + class Geode; + class Geometry; + class Group; + class PositionAttitudeTransform; +} + +namespace CSMWorld +{ + struct Cell; + class CellCoordinates; + class Data; +} + +namespace CSVRender +{ + /// For exterior cells, this adds a patch of water to fit the size of the cell. For interior cells with water, this + /// adds a large patch of water much larger than the typical size of a cell. + class CellWater : public QObject + { + Q_OBJECT + + public: + + CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, + const CSMWorld::CellCoordinates& cellCoords); + + ~CellWater(); + + void updateCellData(const CSMWorld::Record& cellRecord); + + private slots: + + void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); + + private: + + void recreate(); + + static const int CellSize; + + CSMWorld::Data& mData; + std::string mId; + + osg::Group* mParentNode; + + osg::ref_ptr mWaterTransform; + osg::ref_ptr mWaterNode; + osg::ref_ptr mWaterGeometry; + + bool mDeleted; + bool mExterior; + bool mHasWater; + }; +} + +#endif diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ab2e252af..a9d71626d 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -19,6 +19,7 @@ #include "editmode.hpp" #include "mask.hpp" +#include "cameracontroller.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { @@ -512,12 +513,15 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) { char ignore1; // : or ; char ignore2; // # + // Current coordinate int x, y; + // Loop throught all the coordinates to add them to selection while (stream >> ignore1 >> ignore2 >> x >> y) selection.add (CSMWorld::CellCoordinates (x, y)); - - /// \todo adjust camera position + + // Mark that camera needs setup + mCamPositionSet=false; } } else if (hint[0]=='r') diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index b963c6144..0663d3424 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -84,7 +84,8 @@ namespace CSVRender /// hint system. virtual ~PagedWorldspaceWidget(); - + + /// Decodes the the hint string to set of cell that are rendered. void useViewHint (const std::string& hint); void setCellSelection(const CSMWorld::CellSelection& selection); diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 723d93c00..c5d9cdfce 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -109,14 +109,14 @@ namespace CSVRender LightingBright mLightingBright; int mPrevMouseX, mPrevMouseY; + + /// Tells update that camera isn't set + bool mCamPositionSet; FreeCameraController* mFreeCamControl; OrbitCameraController* mOrbitCamControl; CameraController* mCurrentCamControl; - private: - bool mCamPositionSet; - public slots: void update(double dt); diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 4f0356259..a38d301ea 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -21,10 +20,11 @@ #ifdef __linux__ #include +#include #ifndef PR_SET_PTRACER #define PR_SET_PTRACER 0x59616d61 #endif -#elif defined (__APPLE__) || defined (__FreeBSD__) +#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__) #include #endif diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a0e9da26a..0f414972f 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -159,12 +159,14 @@ namespace MWBase virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; ///< Forces an object to refresh its animation state. - virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; + virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1, bool persist=false) = 0; ///< Run animation for a MW-reference. Calls to this function for references that are currently not /// in the scene should be ignored. /// /// \param mode 0 normal, 1 immediate start, 2 immediate loop /// \param count How many times the animation should be run + /// \param persist Whether the animation state should be stored in saved games + /// and persist after cell unload. /// \return Success or error virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; @@ -173,6 +175,9 @@ namespace MWBase virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; + /// Save the current animation state of managed references to their RefData. + virtual void persistAnimationStates() = 0; + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1476e0b48..a757ca2ac 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1400,12 +1400,12 @@ namespace MWMechanics iter->second->getCharacterController()->forceStateUpdate(); } - bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) { - return iter->second->getCharacterController()->playGroup(groupName, mode, number); + return iter->second->getCharacterController()->playGroup(groupName, mode, number, persist); } else { @@ -1428,6 +1428,12 @@ namespace MWMechanics return false; } + void Actors::persistAnimationStates() + { + for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + iter->second->getCharacterController()->persistAnimationState(); + } + void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out) { for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 0dc684c56..163995f6f 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -109,9 +109,10 @@ namespace MWMechanics void forceStateUpdate(const MWWorld::Ptr &ptr); - bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false); void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + void persistAnimationStates(); void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 081f288d5..07aba2f7d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -762,12 +762,17 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); mAnimation->runAnimation(0.f); + + unpersistAnimationState(); } CharacterController::~CharacterController() { if (mAnimation) + { + persistAnimationState(); mAnimation->setTextKeyListener(NULL); + } } void split(const std::string &s, char delim, std::vector &elems) { @@ -1557,14 +1562,14 @@ void CharacterController::update(float duration) { if(mAnimQueue.size() > 1) { - if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) { - mAnimation->disable(mAnimQueue.front().first); + mAnimation->disable(mAnimQueue.front().mGroup); mAnimQueue.pop_front(); - mAnimation->play(mAnimQueue.front().first, Priority_Default, + mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, MWRender::Animation::BlendMask_All, false, - 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount); } } } @@ -1837,14 +1842,14 @@ void CharacterController::update(float duration) } else if(mAnimQueue.size() > 1) { - if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) { - mAnimation->disable(mAnimQueue.front().first); + mAnimation->disable(mAnimQueue.front().mGroup); mAnimQueue.pop_front(); - mAnimation->play(mAnimQueue.front().first, Priority_Default, + mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, MWRender::Animation::BlendMask_All, false, - 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount); } } @@ -1951,8 +1956,74 @@ void CharacterController::update(float duration) mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead()); } +void CharacterController::persistAnimationState() +{ + ESM::AnimationState& state = mPtr.getRefData().getAnimationState(); + + state.mScriptedAnims.clear(); + for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter) + { + if (!iter->mPersist) + continue; + + ESM::AnimationState::ScriptedAnimation anim; + anim.mGroup = iter->mGroup; + + if (iter == mAnimQueue.begin()) + { + anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup); + float complete; + mAnimation->getInfo(anim.mGroup, &complete, NULL); + anim.mTime = complete; + } + else + { + anim.mLoopCount = iter->mLoopCount; + anim.mTime = 0.f; + } + + state.mScriptedAnims.push_back(anim); + } +} + +void CharacterController::unpersistAnimationState() +{ + const ESM::AnimationState& state = mPtr.getRefData().getAnimationState(); + + if (!state.mScriptedAnims.empty()) + { + clearAnimQueue(); + for (ESM::AnimationState::ScriptedAnimations::const_iterator iter = state.mScriptedAnims.begin(); iter != state.mScriptedAnims.end(); ++iter) + { + AnimationQueueEntry entry; + entry.mGroup = iter->mGroup; + entry.mLoopCount = iter->mLoopCount; + entry.mPersist = true; + + mAnimQueue.push_back(entry); + } + + const ESM::AnimationState::ScriptedAnimation& anim = state.mScriptedAnims.front(); + float complete = anim.mTime; + if (anim.mAbsolute) + { + float start = mAnimation->getTextKeyTime(anim.mGroup+": start"); + float stop = mAnimation->getTextKeyTime(anim.mGroup+": stop"); + float time = std::max(start, std::min(stop, anim.mTime)); + complete = (time - start) / (stop - start); + } -bool CharacterController::playGroup(const std::string &groupname, int mode, int count) + mAnimation->disable(mCurrentIdle); + mCurrentIdle.clear(); + mIdleState = CharState_SpecialIdle; + + mAnimation->play(anim.mGroup, + Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, + "start", "stop", complete, anim.mLoopCount); + } +} + +bool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist) { if(!mAnimation || !mAnimation->hasAnimation(groupname)) { @@ -1962,10 +2033,16 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int else { count = std::max(count, 1); - if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().first)) + + AnimationQueueEntry entry; + entry.mGroup = groupname; + entry.mLoopCount = count-1; + entry.mPersist = persist; + + if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup)) { clearAnimQueue(); - mAnimQueue.push_back(std::make_pair(groupname, count-1)); + mAnimQueue.push_back(entry); mAnimation->disable(mCurrentIdle); mCurrentIdle.clear(); @@ -1978,9 +2055,9 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int else if(mode == 0) { if (!mAnimQueue.empty()) - mAnimation->stopLooping(mAnimQueue.front().first); + mAnimation->stopLooping(mAnimQueue.front().mGroup); mAnimQueue.resize(1); - mAnimQueue.push_back(std::make_pair(groupname, count-1)); + mAnimQueue.push_back(entry); } } return true; @@ -2002,7 +2079,7 @@ bool CharacterController::isAnimPlaying(const std::string &groupName) void CharacterController::clearAnimQueue() { if(!mAnimQueue.empty()) - mAnimation->disable(mAnimQueue.front().first); + mAnimation->disable(mAnimQueue.front().mGroup); mAnimQueue.clear(); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index b7e5feb6f..190e171b3 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -147,7 +147,13 @@ class CharacterController : public MWRender::Animation::TextKeyListener MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - typedef std::deque > AnimationQueue; + struct AnimationQueueEntry + { + std::string mGroup; + size_t mLoopCount; + bool mPersist; + }; + typedef std::deque AnimationQueue; AnimationQueue mAnimQueue; CharacterState mIdleState; @@ -236,7 +242,10 @@ public: void update(float duration); - bool playGroup(const std::string &groupname, int mode, int count); + void persistAnimationState(); + void unpersistAnimationState(); + + bool playGroup(const std::string &groupname, int mode, int count, bool persist=false); void skipAnim(); bool isAnimPlaying(const std::string &groupName); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 278a5749e..b10127f74 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -842,12 +842,12 @@ namespace MWMechanics mActors.forceStateUpdate(ptr); } - bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist) { if(ptr.getClass().isActor()) - return mActors.playAnimationGroup(ptr, groupName, mode, number); + return mActors.playAnimationGroup(ptr, groupName, mode, number, persist); else - return mObjects.playAnimationGroup(ptr, groupName, mode, number); + return mObjects.playAnimationGroup(ptr, groupName, mode, number, persist); } void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr) { @@ -864,6 +864,12 @@ namespace MWMechanics return false; } + void MechanicsManager::persistAnimationStates() + { + mActors.persistAnimationStates(); + mObjects.persistAnimationStates(); + } + void MechanicsManager::updateMagicEffects(const MWWorld::Ptr &ptr) { mActors.updateMagicEffects(ptr); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index b0a9380e5..04c67fcb6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -146,9 +146,10 @@ namespace MWMechanics /// Attempt to play an animation group /// @return Success or error - virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false); virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); + virtual void persistAnimationStates(); /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 7d7c0bfae..139825c21 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -79,12 +79,12 @@ void Objects::update(float duration, bool paused) } } -bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) +bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) { - return iter->second->playGroup(groupName, mode, number); + return iter->second->playGroup(groupName, mode, number, persist); } else { @@ -99,6 +99,12 @@ void Objects::skipAnimation(const MWWorld::Ptr& ptr) iter->second->skipAnim(); } +void Objects::persistAnimationStates() +{ + for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) + iter->second->persistAnimationState(); +} + void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out) { for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 07e00675c..1efebafbe 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -38,8 +38,9 @@ namespace MWMechanics void update(float duration, bool paused); ///< Update object animations - bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false); void skipAnimation(const MWWorld::Ptr& ptr); + void persistAnimationStates(); void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& out); }; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fa7542060..0412e1e41 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -843,6 +843,15 @@ namespace MWRender return iter->second.getTime(); } + size_t Animation::getCurrentLoopCount(const std::string& groupname) const + { + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + return 0; + + return iter->second.mLoopCount; + } + void Animation::disable(const std::string &groupname) { AnimStateMap::iterator iter = mStates.find(groupname); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index e9d79ced1..ad9d4ab4a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -415,6 +415,8 @@ public: /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. float getCurrentTime(const std::string& groupname) const; + size_t getCurrentLoopCount(const std::string& groupname) const; + /** Disables the specified animation group; * \param groupname Animation group to disable. */ diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index ab828a3ad..532e5cedc 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -40,58 +42,6 @@ #include "renderbin.hpp" #include "util.hpp" -namespace -{ - - osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) - { - osg::ref_ptr verts (new osg::Vec3Array); - osg::ref_ptr texcoords (new osg::Vec2Array); - - // some drivers don't like huge triangles, so we do some subdivisons - // a paged solution would be even better - const float step = size/segments; - const float texCoordStep = textureRepeats / segments; - for (int x=0; xpush_back(osg::Vec3f(x1, y2, 0.f)); - verts->push_back(osg::Vec3f(x1, y1, 0.f)); - verts->push_back(osg::Vec3f(x2, y1, 0.f)); - verts->push_back(osg::Vec3f(x2, y2, 0.f)); - - float u1 = x*texCoordStep; - float v1 = y*texCoordStep; - float u2 = u1 + texCoordStep; - float v2 = v1 + texCoordStep; - - texcoords->push_back(osg::Vec2f(u1, v2)); - texcoords->push_back(osg::Vec2f(u1, v1)); - texcoords->push_back(osg::Vec2f(u2, v1)); - texcoords->push_back(osg::Vec2f(u2, v2)); - } - } - - osg::ref_ptr waterGeom (new osg::Geometry); - waterGeom->setVertexArray(verts); - waterGeom->setTexCoordArray(0, texcoords); - - osg::ref_ptr normal (new osg::Vec3Array); - normal->push_back(osg::Vec3f(0,0,1)); - waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); - - waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); - return waterGeom; - } - -} - namespace MWRender { @@ -465,7 +415,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); - mWaterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900); + mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); @@ -527,26 +477,11 @@ void Water::updateWaterMaterial() void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { - osg::ref_ptr stateset (new osg::StateSet); - - osg::ref_ptr material (new osg::Material); - material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); - material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); - material->setColorMode(osg::Material::OFF); - stateset->setAttributeAndModes(material, osg::StateAttribute::ON); - - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - - osg::ref_ptr depth (new osg::Depth); - depth->setWriteMask(false); - stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); - - stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); + osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water); node->setStateSet(stateset); + // Add animated textures std::vector > textures; int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp index 07a8a300a..44c4612ec 100644 --- a/apps/openmw/mwscript/animationextensions.cpp +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -56,7 +56,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits::max()); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits::max(), true); } }; @@ -89,7 +89,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops, true); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a8639b94b..48cc37935 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -219,6 +219,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot else slot = character->updateSlot (slot, profile); + // Make sure the animation state held by references is up to date before saving the game. + MWBase::Environment::get().getMechanicsManager()->persistAnimationStates(); + // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the // existing save file we are overwriting. std::stringstream stream; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index f85abcc32..ebc38ac9b 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -31,6 +31,8 @@ namespace MWWorld mDeletedByContentFile = refData.mDeletedByContentFile; mFlags = refData.mFlags; + mAnimationState = refData.mAnimationState; + mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -65,6 +67,7 @@ namespace MWWorld mEnabled (objectState.mEnabled != 0), mCount (objectState.mCount), mPosition (objectState.mPosition), + mAnimationState(objectState.mAnimationState), mCustomData (0), mChanged(true), mFlags(objectState.mFlags) // Loading from a savegame -> assume changed { @@ -96,6 +99,8 @@ namespace MWWorld objectState.mCount = mCount; objectState.mPosition = mPosition; objectState.mFlags = mFlags; + + objectState.mAnimationState = mAnimationState; } RefData& RefData::operator= (const RefData& refData) @@ -269,4 +274,15 @@ namespace MWWorld else return false; } + + const ESM::AnimationState& RefData::getAnimationState() const + { + return mAnimationState; + } + + ESM::AnimationState& RefData::getAnimationState() + { + return mAnimationState; + } + } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 9e662e430..75eec6742 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -2,6 +2,7 @@ #define GAME_MWWORLD_REFDATA_H #include +#include #include "../mwscript/locals.hpp" @@ -42,6 +43,8 @@ namespace MWWorld ESM::Position mPosition; + ESM::AnimationState mAnimationState; + CustomData *mCustomData; void copy (const RefData& refData); @@ -132,6 +135,9 @@ namespace MWWorld bool hasChanged() const; ///< Has this RefData changed since it was originally loaded? + + const ESM::AnimationState& getAnimationState() const; + ESM::AnimationState& getAnimationState(); }; } diff --git a/apps/wizard/CMakeLists.txt b/apps/wizard/CMakeLists.txt index dcd944490..dd2e1a748 100644 --- a/apps/wizard/CMakeLists.txt +++ b/apps/wizard/CMakeLists.txt @@ -136,7 +136,7 @@ if (BUILD_WITH_CODE_COVERAGE) endif() # Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream -if (UNIX AND NOT APPLE) +if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_link_libraries(openmw-wizard dl Xt) endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4cffd5044..a9feae10f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller - lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil + lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil ) add_component_dir (nif @@ -79,7 +79,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate stolenitems transport + aisequence magiceffects util custommarkerstate stolenitems transport animationstate ) add_component_dir (esmterrain diff --git a/components/esm/animationstate.cpp b/components/esm/animationstate.cpp new file mode 100644 index 000000000..52dde258f --- /dev/null +++ b/components/esm/animationstate.cpp @@ -0,0 +1,37 @@ +#include "animationstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + void AnimationState::load(ESMReader& esm) + { + mScriptedAnims.clear(); + + while (esm.isNextSub("ANIS")) + { + ScriptedAnimation anim; + + anim.mGroup = esm.getHString(); + esm.getHNOT(anim.mTime, "TIME"); + esm.getHNOT(anim.mAbsolute, "ABST"); + esm.getHNT(anim.mLoopCount, "COUN"); + + mScriptedAnims.push_back(anim); + } + } + + void AnimationState::save(ESMWriter& esm) const + { + for (ScriptedAnimations::const_iterator iter = mScriptedAnims.begin(); iter != mScriptedAnims.end(); ++iter) + { + esm.writeHNString("ANIS", iter->mGroup); + if (iter->mTime > 0) + esm.writeHNT("TIME", iter->mTime); + if (iter->mAbsolute) + esm.writeHNT("ABST", iter->mAbsolute); + esm.writeHNT("COUN", iter->mLoopCount); + } + } +} diff --git a/components/esm/animationstate.hpp b/components/esm/animationstate.hpp new file mode 100644 index 000000000..73b6a41d0 --- /dev/null +++ b/components/esm/animationstate.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_ANIMATIONSTATE_H +#define OPENMW_ESM_ANIMATIONSTATE_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + struct AnimationState + { + struct ScriptedAnimation + { + ScriptedAnimation() + : mTime(0.f), mAbsolute(false), mLoopCount(0) {} + + std::string mGroup; + float mTime; + bool mAbsolute; + size_t mLoopCount; + }; + + typedef std::vector ScriptedAnimations; + ScriptedAnimations mScriptedAnims; + + void load(ESMReader& esm); + void save(ESMWriter& esm) const; + }; +} + +#endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index b80c72ffe..18c030256 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -34,6 +34,8 @@ void ESM::ObjectState::load (ESMReader &esm) int unused; esm.getHNOT(unused, "LTIM"); + mAnimationState.load(esm); + // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; esm.getHNOT (mHasCustomState, "HCUS"); @@ -61,6 +63,8 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mFlags != 0) esm.writeHNT ("FLAG", mFlags); + mAnimationState.save(esm); + if (!mHasCustomState) esm.writeHNT ("HCUS", false); } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 5b78074af..b8eb138eb 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -6,6 +6,7 @@ #include "cellref.hpp" #include "locals.hpp" +#include "animationstate.hpp" namespace ESM { @@ -31,6 +32,8 @@ namespace ESM unsigned int mVersion; + ESM::AnimationState mAnimationState; + ObjectState() : mHasCustomState(true), mVersion(0) {} diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 5e0ea6c86..2e72b8154 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -4,7 +4,7 @@ #include #include -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #ifndef ANDROID #include namespace Files { typedef LinuxPath TargetPathType; } diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 212db562c..89b4bf45a 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -1,6 +1,6 @@ #include "linuxpath.hpp" -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #include #include @@ -159,4 +159,4 @@ boost::filesystem::path LinuxPath::getInstallPath() const } /* namespace Files */ -#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ +#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) */ diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index ba9756fc0..7950157bb 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -1,7 +1,7 @@ #ifndef COMPONENTS_FILES_LINUXPATH_H #define COMPONENTS_FILES_LINUXPATH_H -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #include @@ -56,6 +56,6 @@ struct LinuxPath } /* namespace Files */ -#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ +#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) */ #endif /* COMPONENTS_FILES_LINUXPATH_H */ diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp new file mode 100644 index 000000000..562b0ee73 --- /dev/null +++ b/components/sceneutil/waterutil.cpp @@ -0,0 +1,79 @@ +#include "waterutil.hpp" + +#include +#include +#include +#include + +namespace SceneUtil +{ + osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) + { + osg::ref_ptr verts (new osg::Vec3Array); + osg::ref_ptr texcoords (new osg::Vec2Array); + + // some drivers don't like huge triangles, so we do some subdivisons + // a paged solution would be even better + const float step = size/segments; + const float texCoordStep = textureRepeats / segments; + for (int x=0; xpush_back(osg::Vec3f(x1, y2, 0.f)); + verts->push_back(osg::Vec3f(x1, y1, 0.f)); + verts->push_back(osg::Vec3f(x2, y1, 0.f)); + verts->push_back(osg::Vec3f(x2, y2, 0.f)); + + float u1 = x*texCoordStep; + float v1 = y*texCoordStep; + float u2 = u1 + texCoordStep; + float v2 = v1 + texCoordStep; + + texcoords->push_back(osg::Vec2f(u1, v2)); + texcoords->push_back(osg::Vec2f(u1, v1)); + texcoords->push_back(osg::Vec2f(u2, v1)); + texcoords->push_back(osg::Vec2f(u2, v2)); + } + } + + osg::ref_ptr waterGeom (new osg::Geometry); + waterGeom->setVertexArray(verts); + waterGeom->setTexCoordArray(0, texcoords); + + osg::ref_ptr normal (new osg::Vec3Array); + normal->push_back(osg::Vec3f(0,0,1)); + waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); + + waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); + return waterGeom; + } + + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin) + { + osg::ref_ptr stateset (new osg::StateSet); + + osg::ref_ptr material (new osg::Material); + material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); + material->setColorMode(osg::Material::OFF); + stateset->setAttributeAndModes(material, osg::StateAttribute::ON); + + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + + osg::ref_ptr depth (new osg::Depth); + depth->setWriteMask(false); + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); + + stateset->setRenderBinDetails(renderBin, "RenderBin"); + + return stateset; + } +} diff --git a/components/sceneutil/waterutil.hpp b/components/sceneutil/waterutil.hpp new file mode 100644 index 000000000..7b8c38010 --- /dev/null +++ b/components/sceneutil/waterutil.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_COMPONENTS_WATERUTIL_H +#define OPENMW_COMPONENTS_WATERUTIL_H + +#include + +namespace osg +{ + class Geometry; + class StateSet; +} + +namespace SceneUtil +{ + osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats); + + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin); +} + +#endif diff --git a/files/opencs/flying eye.png b/files/opencs/flying-eye.png similarity index 100% rename from files/opencs/flying eye.png rename to files/opencs/flying-eye.png diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index e9fb12cbe..7e798527c 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -77,7 +77,7 @@ Sun-48.png Lightbulb-48.png eyeballdude.png - flying eye.png + flying-eye.png orbit2.png scene-play.png scene-view-references.png