diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 99cbe96541..de5f15d64f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -278,8 +278,12 @@ namespace MWBase virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; - virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; - ///< place an object in a "safe" location (ie not in the void, etc). + virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; + ///< Place an object. Makes a copy of the Ptr. + + virtual MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance) = 0; + ///< Place an object in a safe place next to \a referenceObject. \a direction and \a distance specify the wanted placement + /// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index cd55d31a12..e0e890b604 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -120,7 +120,7 @@ namespace MWClass const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawn = false; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 3299846b7e..61a0efc460 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -67,46 +67,29 @@ namespace MWGui { MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); - if (result == MWMechanics::Alchemy::Result_NoName) + switch (result) { + case MWMechanics::Alchemy::Result_NoName: MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}"); - return; - } - - // check if mortar & pestle is available (always needed) - if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) - { + break; + case MWMechanics::Alchemy::Result_NoMortarAndPestle: MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}"); - return; - } - - // make sure 2 or more ingredients were selected - if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) - { + break; + case MWMechanics::Alchemy::Result_LessThanTwoIngredients: MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}"); - return; - } - - if (result == MWMechanics::Alchemy::Result_NoEffects) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); - MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); - return; - } - - if (result == MWMechanics::Alchemy::Result_Success) - { + break; + case MWMechanics::Alchemy::Result_Success: MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}"); MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f); - } - else if (result == MWMechanics::Alchemy::Result_RandomFailure) - { - // potion failed + break; + case MWMechanics::Alchemy::Result_NoEffects: + case MWMechanics::Alchemy::Result_RandomFailure: MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); + break; } - // reduce count of the ingredients + // remove ingredient slots that have been fully used up for (int i=0; i<4; ++i) if (mIngredients[i]->isUserString("ToolTipType")) { diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 636e199074..cf77714855 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -114,31 +114,16 @@ namespace MWMechanics bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); if (!found) { - ESM::Position ipos = mActor.getRefData().getPosition(); - osg::Vec3f pos(ipos.asVec3()); - - osg::Quat rot (-ipos.rot[2], osg::Vec3f(0,0,1)); - const float distance = 50; - pos = pos + (rot * osg::Vec3f(0,1,0)) * distance; - ipos.pos[0] = pos.x(); - ipos.pos[1] = pos.y(); - ipos.pos[2] = pos.z(); - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - const std::string& creatureGmst = summonMap[it->first]; std::string creatureID = MWBase::Environment::get().getWorld()->getStore().get().find(creatureGmst)->getString(); if (!creatureID.empty()) { - MWWorld::CellStore* store = mActor.getCell(); int creatureActorId = -1; try { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); - ref.getPtr().getCellRef().setPosition(ipos); MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); @@ -147,7 +132,7 @@ namespace MWMechanics summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); creatureActorId = summonedCreatureStats.getActorId(); - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), mActor, mActor.getCell(), 0, 120.f); MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); if (anim) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0418e34fdc..7f7183b9e0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1353,9 +1353,24 @@ namespace MWRender if (found != getNodeMap().end()) { osg::MatrixTransform* node = found->second; - mHeadController = new RotateController(mObjectRoot.get()); - node->addUpdateCallback(mHeadController); - mActiveControllers.insert(std::make_pair(node, mHeadController)); + + bool foundKeyframeCtrl = false; + osg::Callback* cb = node->getUpdateCallback(); + while (cb) + { + if (dynamic_cast(cb)) + { + foundKeyframeCtrl = true; + break; + } + } + + if (foundKeyframeCtrl) + { + mHeadController = new RotateController(mObjectRoot.get()); + node->addUpdateCallback(mHeadController); + mActiveControllers.insert(std::make_pair(node, mHeadController)); + } } } } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0a379e50ac..340c077854 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 95e2deee9b..e9e13e74f6 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -434,7 +434,7 @@ namespace MWScript pos.rot[2] = osg::DegreesToRadians(zRotDegrees); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos); placed.getClass().adjustPosition(placed, true); } } @@ -482,7 +482,7 @@ namespace MWScript pos.rot[2] = osg::DegreesToRadians(zRotDegrees); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos); placed.getClass().adjustPosition(placed, true); } }; @@ -516,38 +516,10 @@ namespace MWScript for (int i=0; igetStore(), itemID, 1); - ref.getPtr().getCellRef().setPosition(ipos); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance); } } }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 63bb4758a3..ba28294690 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -268,6 +268,13 @@ namespace MWWorld mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts); } + else + { + static std::vector defaultHeight; + defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); + mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), + worldsize / (verts-1), verts); + } } // register local scripts diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6b8f25a2c..2ab027cd63 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1325,11 +1325,50 @@ namespace MWWorld rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust); } - MWWorld::Ptr World::safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) + MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) { return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false); } + MWWorld::Ptr World::safePlaceObject(const ConstPtr &ptr, const ConstPtr &referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance) + { + ESM::Position ipos = referenceObject.getRefData().getPosition(); + osg::Vec3f pos(ipos.asVec3()); + osg::Quat orientation(ipos.rot[2], osg::Vec3f(0,0,-1)); + + int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4}; + + for (int i=0; i<4; ++i) + { + direction = fallbackDirections[i]; + // check if spawn point is safe, fall back to another direction if not + osg::Vec3f spawnPoint = pos; + if (direction == 0) spawnPoint = pos + (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 1) spawnPoint = pos - (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 2) spawnPoint = pos - (orientation * osg::Vec3f(1,0,0)) * distance; + else if(direction == 3) spawnPoint = pos + (orientation * osg::Vec3f(1,0,0)) * distance; + spawnPoint.z() += 30; // move up a little to account for slopes, will snap down later + + if (!castRay(spawnPoint.x(), spawnPoint.y(), spawnPoint.z(), + pos.x(), pos.y(), pos.z() + 20)) + { + // safe + ipos.pos[0] = spawnPoint.x(); + ipos.pos[1] = spawnPoint.y(); + ipos.pos[2] = spawnPoint.z(); + break; + } + } + + ipos.rot[0] = referenceObject.getRefData().getPosition().rot[0]; + ipos.rot[1] = referenceObject.getRefData().getPosition().rot[1]; + ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2]; + + MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false); + placed.getClass().adjustPosition(placed, true); // snap to ground + return placed; + } + void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { const int cellSize = 8192; @@ -3025,23 +3064,9 @@ namespace MWWorld if (selectedCreature.empty()) return; - ESM::Position ipos = mPlayer->getPlayer().getRefData().getPosition(); - osg::Vec3f pos(ipos.asVec3()); - osg::Quat rot(-ipos.rot[2], osg::Vec3f(0,0,1)); - const float distance = 50; - pos = pos + (rot * osg::Vec3f(0,1,0)) * distance; - ipos.pos[0] = pos.x(); - ipos.pos[1] = pos.y(); - ipos.pos[2] = pos.z(); - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - - MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); MWWorld::ManualRef ref(getStore(), selectedCreature, 1); - ref.getPtr().getCellRef().setPosition(ipos); - safePlaceObject(ref.getPtr(), cell, ipos); + safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ce5aa417ed..d1298a39be 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -367,8 +367,12 @@ namespace MWWorld /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); - virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos); - ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos); + ///< Place an object. Makes a copy of the Ptr. + + virtual MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance); + ///< Place an object in a safe place next to \a referenceObject. \a direction and \a distance specify the wanted placement + /// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed). virtual float getMaxActivationDistance(); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 8a8d6fdd2f..a2bf1573e9 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -46,6 +46,9 @@ struct Land DATA_VTEX = 16 }; + // default height to use in case there is no Land record + static const int DEFAULT_HEIGHT = -2048; + // number of vertices per side static const int LAND_SIZE = 65; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 68b32cf2a1..86d1e08e64 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,7 +16,7 @@ namespace ESMTerrain { - const float defaultHeight = -2048; + const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) : mVFS(vfs) diff --git a/components/sceneutil/attach.cpp b/components/sceneutil/attach.cpp index d8cfa428a2..38330901d3 100644 --- a/components/sceneutil/attach.cpp +++ b/components/sceneutil/attach.cpp @@ -56,9 +56,18 @@ namespace SceneUtil CopyRigVisitor copyVisitor(handle, filter); toAttach->accept(copyVisitor); - master->asGroup()->addChild(handle); - - return handle; + if (handle->getNumChildren() == 1) + { + osg::ref_ptr newHandle = handle->getChild(0); + handle->removeChild(newHandle); + master->asGroup()->addChild(newHandle); + return newHandle; + } + else + { + master->asGroup()->addChild(handle); + return handle; + } } else { diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index d1299c0587..f311de2469 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -38,6 +38,8 @@ Skeleton::Skeleton() , mNeedToUpdateBoneMatrices(true) , mActive(true) , mLastFrameNumber(0) + , mTraversedEvenFrame(false) + , mTraversedOddFrame(false) { } @@ -48,6 +50,8 @@ Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op) , mNeedToUpdateBoneMatrices(true) , mActive(copy.mActive) , mLastFrameNumber(0) + , mTraversedEvenFrame(false) + , mTraversedOddFrame(false) { } @@ -111,6 +115,11 @@ void Skeleton::updateBoneMatrices(osg::NodeVisitor* nv) mLastFrameNumber = nv->getTraversalNumber(); + if (mLastFrameNumber % 2 == 0) + mTraversedEvenFrame = true; + else + mTraversedOddFrame = true; + if (mNeedToUpdateBoneMatrices) { if (mRootBone.get()) @@ -140,7 +149,7 @@ void Skeleton::traverse(osg::NodeVisitor& nv) if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR // need to process at least 2 frames before shutting off update, since we need to have both frame-alternating RigGeometries initialized // this would be more naturally handled if the double-buffering was implemented in RigGeometry itself rather than in a FrameSwitch decorator node - && mLastFrameNumber != 0 && mLastFrameNumber+2 <= nv.getTraversalNumber()) + && mLastFrameNumber != 0 && mTraversedEvenFrame && mTraversedOddFrame) return; osg::Group::traverse(nv); } diff --git a/components/sceneutil/skeleton.hpp b/components/sceneutil/skeleton.hpp index d98d367518..9ca1dd49da 100644 --- a/components/sceneutil/skeleton.hpp +++ b/components/sceneutil/skeleton.hpp @@ -69,6 +69,8 @@ namespace SceneUtil bool mActive; unsigned int mLastFrameNumber; + bool mTraversedEvenFrame; + bool mTraversedOddFrame; }; }