diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2d49a414c..3dabc9ac8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects - renderinginterface localmap water terrain terrainmaterial + renderinginterface localmap occlusionquery terrain terrainmaterial water ) add_openmw_dir (mwinput diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 441c22769..38050e53b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -204,13 +204,18 @@ OMW::Engine::~Engine() void OMW::Engine::loadBSA() { const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa"); - std::string dataDirectory; + for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter) { std::cout << "Adding " << iter->second.string() << std::endl; Bsa::addBSA(iter->second.string()); + } - dataDirectory = iter->second.parent_path().string(); + const Files::PathContainer& dataDirs = mFileCollections.getPaths(); + std::string dataDirectory; + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + { + dataDirectory = iter->string(); std::cout << "Data dir " << dataDirectory << std::endl; Bsa::addDir(dataDirectory, mFSStrict); } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index e95fb572f..d27d0bc71 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -56,7 +56,7 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e1c2734f0..9956a56fb 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -60,7 +60,7 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0a81ebafb..76370dc5c 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -58,7 +58,7 @@ namespace MWClass { // TODO implement reading - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 4fe19ada4..2357851d7 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -57,7 +57,7 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index c58a25c03..29b3331ba 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -85,7 +85,7 @@ namespace MWClass { // TODO check for key std::cout << "Locked container" << std::endl; - environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0); return boost::shared_ptr (new MWWorld::NullAction); } else @@ -100,7 +100,7 @@ namespace MWClass { // Trap activation goes here std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false); + environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0); ptr.getCellRef().trap = ""; return boost::shared_ptr (new MWWorld::NullAction); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 5654dff69..9d6c6a78d 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -73,7 +73,7 @@ namespace MWClass // TODO check for key // TODO report failure to player (message, sound?). Look up behaviour of original MW. std::cout << "Locked!" << std::endl; - environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0); return boost::shared_ptr (new MWWorld::NullAction); } @@ -81,7 +81,7 @@ namespace MWClass { // Trap activation std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false); + environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0); ptr.getCellRef().trap = ""; return boost::shared_ptr (new MWWorld::NullAction); } @@ -110,7 +110,7 @@ namespace MWClass // TODO return action for rotating the door // This is a little pointless, but helps with testing - environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false); + environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0); return boost::shared_ptr (new MWWorld::NullAction); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 1a7edf632..cbe153ba3 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -54,7 +54,7 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index e2e63a89b..71e477591 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -59,7 +59,7 @@ namespace MWClass if (!ref->base->sound.empty()) { - environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, true); + environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); } } @@ -83,7 +83,7 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 3dda2f4af..1eef0db8b 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -58,7 +58,7 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 864fc1e38..def1a90a8 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -56,7 +56,7 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 4ab374590..ed1733e2d 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -56,7 +56,7 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 4b4d79a73..8013e2e80 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -57,7 +57,7 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 758bf4079..d49979861 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -56,7 +56,7 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 20db0cf38..e36e9202f 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -57,7 +57,7 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { - environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 465467469..49b6e644d 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -180,71 +180,58 @@ void WindowManager::updateVisible() // Mouse is visible whenever we're not in game mode MyGUI::PointerManager::getInstance().setVisible(isGuiMode()); - // If in game mode, don't show anything. - if(mode == GM_Game) //Use a switch/case structure - { - return; - } + switch(mode) { + case GM_Game: + // If in game mode, don't show anything. + break; + case GM_MainMenu: + menu->setVisible(true); + break; + case GM_Console: + console->enable(); + break; + case GM_Name: + case GM_Race: + case GM_Class: + case GM_ClassPick: + case GM_ClassCreate: + case GM_Birth: + case GM_ClassGenerate: + case GM_Review: + mCharGen->spawnDialog(mode); + break; + case GM_Inventory: + { + // First, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = shown & allowed; - if(mode == GM_MainMenu) - { - // Enable the main menu - menu->setVisible(true); - return; - } - - if(mode == GM_Console) - { - console->enable(); - return; - } - - //There must be a more elegant solution - if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review) - { - mCharGen->spawnDialog(mode); - return; - } - - if(mode == GM_Inventory) - { - // Ah, inventory mode. First, compute the effective set of - // windows to show. This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = shown & allowed; - - // Show the windows we want - map -> setVisible( (eff & GW_Map) != 0 ); - stats -> setVisible( (eff & GW_Stats) != 0 ); - return; - } - - if (mode == GM_Dialogue) - { - dialogueWindow->open(); - return; - } - - if(mode == GM_InterMessageBox) - { - if(!mMessageBoxManager->isInteractiveMessageBox()) { - setGuiMode(GM_Game); + // Show the windows we want + map -> setVisible( (eff & GW_Map) != 0 ); + stats -> setVisible( (eff & GW_Stats) != 0 ); + break; } - return; + case GM_Dialogue: + dialogueWindow->open(); + break; + case GM_InterMessageBox: + if(!mMessageBoxManager->isInteractiveMessageBox()) { + setGuiMode(GM_Game); + } + break; + case GM_Journal: + mJournal->setVisible(true); + mJournal->open(); + break; + default: + // Unsupported mode, switch back to game + // Note: The call will eventually end up this method again but + // will stop at the check if mode is GM_Game. + setGuiMode(GM_Game); + break; } - - if(mode == GM_Journal) - { - mJournal->setVisible(true); - mJournal->open(); - return; - } - - // Unsupported mode, switch back to game - // Note: The call will eventually end up this method again but - // will stop at the check if(mode == GM_Game) above. - setGuiMode(GM_Game); } void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f3a8f64d5..fb710443b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -126,6 +126,11 @@ namespace MWRender{ void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){ shapeNumber = 0; + if (allshapes == NULL || creaturemodel == NULL || skel == NULL) + { + return; + } + std::vector::iterator allshapesiter; for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 60b299acd..422185273 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -27,11 +27,7 @@ bool Debugging::toggleRenderMode (int mode){ switch (mode) { case MWWorld::World::Render_CollisionDebug: - - // TODO use a proper function instead of accessing the member variable - // directly. - eng->setDebugRenderingMode (!eng->isDebugCreated); - return eng->isDebugCreated; + return eng->toggleDebugRendering(); } return false; diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp new file mode 100644 index 000000000..cc3464c64 --- /dev/null +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -0,0 +1,254 @@ +#include "occlusionquery.hpp" + +#include +#include +#include +#include +#include + +using namespace MWRender; +using namespace Ogre; + +OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) : + mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mSingleObjectQuery(0), mActiveQuery(0), + mDoQuery(0), mSunVisibility(0), mQuerySingleObjectStarted(false), mTestResult(false), + mQuerySingleObjectRequested(false), mWasVisible(false), mObjectWasVisible(false), mDoQuery2(false) +{ + mRendering = renderer; + mSunNode = sunNode; + + try { + RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); + + mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery(); + mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery(); + mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery(); + + mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0); + } + catch (Ogre::Exception e) + { + mSupported = false; + } + + if (!mSupported) + { + std::cout << "Hardware occlusion queries not supported." << std::endl; + return; + } + + // This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested + const int queue = RENDER_QUEUE_MAIN+1; + + MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting"); + MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels"); + matQueryArea->setDepthWriteEnabled(false); + matQueryArea->setColourWriteEnabled(false); + matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects + MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels"); + matQueryVisible->setDepthWriteEnabled(false); + matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query + matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects + matQueryVisible->setCullingMode(CULL_NONE); + matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE); + + mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode(); + + mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); + mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); + + mBBQueryTotal = mRendering->getScene()->createBillboardSet(1); + mBBQueryTotal->setDefaultDimensions(150, 150); + mBBQueryTotal->createBillboard(Vector3::ZERO); + mBBQueryTotal->setMaterialName("QueryTotalPixels"); + mBBQueryTotal->setRenderQueueGroup(queue+1); + mBBNodeReal->attachObject(mBBQueryTotal); + + mBBQueryVisible = mRendering->getScene()->createBillboardSet(1); + mBBQueryVisible->setDefaultDimensions(150, 150); + mBBQueryVisible->createBillboard(Vector3::ZERO); + mBBQueryVisible->setMaterialName("QueryVisiblePixels"); + mBBQueryVisible->setRenderQueueGroup(queue+1); + mBBNodeReal->attachObject(mBBQueryVisible); + + mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1); + /// \todo ideally this should occupy exactly 1 pixel on the screen + mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003); + mBBQuerySingleObject->createBillboard(Vector3::ZERO); + mBBQuerySingleObject->setMaterialName("QueryVisiblePixels"); + mBBQuerySingleObject->setRenderQueueGroup(queue); + mObjectNode->attachObject(mBBQuerySingleObject); + + mRendering->getScene()->addRenderObjectListener(this); + mRendering->getScene()->addRenderQueueListener(this); + mDoQuery = true; + mDoQuery2 = true; +} + +OcclusionQuery::~OcclusionQuery() +{ + RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); + if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery); + if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery); + if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery); +} + +bool OcclusionQuery::supported() +{ + return mSupported; +} + +void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source, + const LightList* pLightList, bool suppressRenderStateChanges) +{ + // The following code activates and deactivates the occlusion queries + // so that the queries only include the rendering of their intended targets + + // Close the last occlusion query + // Each occlusion query should only last a single rendering + if (mActiveQuery != NULL) + { + mActiveQuery->endOcclusionQuery(); + mActiveQuery = NULL; + } + + // Open a new occlusion query + if (mDoQuery == true) + { + if (rend == mBBQueryTotal) + { + mActiveQuery = mSunTotalAreaQuery; + mWasVisible = true; + } + else if (rend == mBBQueryVisible) + { + mActiveQuery = mSunVisibleAreaQuery; + } + } + if (mDoQuery == true && rend == mBBQuerySingleObject) + { + mQuerySingleObjectStarted = true; + mQuerySingleObjectRequested = false; + mActiveQuery = mSingleObjectQuery; + mObjectWasVisible = true; + } + + if (mActiveQuery != NULL) + mActiveQuery->beginOcclusionQuery(); +} + +void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation) +{ + if (mActiveQuery != NULL) + { + mActiveQuery->endOcclusionQuery(); + mActiveQuery = NULL; + } + /** + * for every beginOcclusionQuery(), we want a respective pullOcclusionQuery() and vice versa + * this also means that results can be wrong at other places if we pull, but beginOcclusionQuery() was never called + * this can happen for example if the object that is tested is outside of the view frustum + * to prevent this, check if the queries have been performed after everything has been rendered and if not, start them manually + */ + if (queueGroupId == RENDER_QUEUE_SKIES_LATE) + { + if (mWasVisible == false && mDoQuery) + { + mSunTotalAreaQuery->beginOcclusionQuery(); + mSunTotalAreaQuery->endOcclusionQuery(); + mSunVisibleAreaQuery->beginOcclusionQuery(); + mSunVisibleAreaQuery->endOcclusionQuery(); + } + if (mObjectWasVisible == false && mDoQuery) + { + mSingleObjectQuery->beginOcclusionQuery(); + mSingleObjectQuery->endOcclusionQuery(); + mQuerySingleObjectStarted = true; + mQuerySingleObjectRequested = false; + } + } +} + +void OcclusionQuery::update(float duration) +{ + if (!mSupported) return; + + mWasVisible = false; + mObjectWasVisible = false; + + // Adjust the position of the sun billboards according to camera viewing distance + // we need to do this to make sure that _everything_ can occlude the sun + float dist = mRendering->getCamera()->getFarClipDistance(); + if (dist==0) dist = 10000000; + dist -= 1000; // bias + dist /= 1000.f; + mBBNode->setPosition(mSunNode->getPosition() * dist); + mBBNode->setScale(dist, dist, dist); + mBBNodeReal->setPosition(mBBNode->_getDerivedPosition()); + mBBNodeReal->setScale(mBBNode->getScale()); + + // Stop occlusion queries until we get their information + // (may not happen on the same frame they are requested in) + mDoQuery = false; + mDoQuery2 = false; + + if (!mSunTotalAreaQuery->isStillOutstanding() + && !mSunVisibleAreaQuery->isStillOutstanding() + && !mSingleObjectQuery->isStillOutstanding()) + { + unsigned int totalPixels; + unsigned int visiblePixels; + + mSunTotalAreaQuery->pullOcclusionQuery(&totalPixels); + mSunVisibleAreaQuery->pullOcclusionQuery(&visiblePixels); + + if (totalPixels == 0) + { + // probably outside of the view frustum + mSunVisibility = 0; + } + else + { + mSunVisibility = float(visiblePixels) / float(totalPixels); + if (mSunVisibility > 1) mSunVisibility = 1; + } + + unsigned int result; + + mSingleObjectQuery->pullOcclusionQuery(&result); + + mTestResult = (result != 0); + + mQuerySingleObjectStarted = false; + mQuerySingleObjectRequested = false; + + mDoQuery = true; + } +} + +void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object) +{ + assert( !occlusionTestPending() + && "Occlusion test still pending"); + + mBBQuerySingleObject->setVisible(true); + + mObjectNode->setPosition(position); + // scale proportional to camera distance, in order to always give the billboard the same size in screen-space + mObjectNode->setScale( Vector3(1,1,1)*(position - mRendering->getCamera()->getRealPosition()).length() ); + + mQuerySingleObjectRequested = true; +} + +bool OcclusionQuery::occlusionTestPending() +{ + return (mQuerySingleObjectRequested || mQuerySingleObjectStarted); +} + +bool OcclusionQuery::getTestResult() +{ + assert( !occlusionTestPending() + && "Occlusion test still pending"); + + return mTestResult; +} diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp new file mode 100644 index 000000000..ebdc51311 --- /dev/null +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -0,0 +1,95 @@ +#ifndef _GAME_OCCLUSION_QUERY_H +#define _GAME_OCCLUSION_QUERY_H + +#include +#include + +namespace Ogre +{ + class HardwareOcclusionQuery; + class Entity; + class SceneNode; +} + +#include + +namespace MWRender +{ + /// + /// \brief Implements hardware occlusion queries on the GPU + /// + class OcclusionQuery : public Ogre::RenderObjectListener, public Ogre::RenderQueueListener + { + public: + OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode); + ~OcclusionQuery(); + + /** + * @return true if occlusion queries are supported on the user's hardware + */ + bool supported(); + + /** + * per-frame update + */ + void update(float duration); + + /** + * request occlusion test for a billboard at the given position, omitting an entity + * @param position of the billboard in ogre coordinates + * @param object to exclude from the occluders + */ + void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object); + + /** + * @return true if a request is still outstanding + */ + bool occlusionTestPending(); + + /** + * @return true if the object tested in the last request was occluded + */ + bool getTestResult(); + + float getSunVisibility() const {return mSunVisibility;}; + + private: + Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery; + Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery; + Ogre::HardwareOcclusionQuery* mSingleObjectQuery; + Ogre::HardwareOcclusionQuery* mActiveQuery; + + Ogre::BillboardSet* mBBQueryVisible; + Ogre::BillboardSet* mBBQueryTotal; + Ogre::BillboardSet* mBBQuerySingleObject; + + Ogre::SceneNode* mSunNode; + Ogre::SceneNode* mBBNode; + Ogre::SceneNode* mBBNodeReal; + float mSunVisibility; + + Ogre::SceneNode* mObjectNode; + + bool mWasVisible; + bool mObjectWasVisible; + + bool mTestResult; + + bool mSupported; + bool mDoQuery; + bool mDoQuery2; + + bool mQuerySingleObjectRequested; + bool mQuerySingleObjectStarted; + + OEngine::Render::OgreRenderer* mRendering; + + protected: + virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source, + const Ogre::LightList* pLightList, bool suppressRenderStateChanges); + + virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation); + }; +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c947b67f5..3d715b3d3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -46,9 +46,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mMwRoot->pitch(Degree(-90)); mObjects.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot); - - //used to obtain ingame information of ogre objects (which are faced or selected) - mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray()); Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); playerNode->pitch(Degree(90)); @@ -59,10 +56,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const //mSkyManager = 0; mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); + mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); mWater = 0; - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; @@ -76,6 +73,7 @@ RenderingManager::~RenderingManager () delete mSkyManager; delete mTerrainManager; delete mLocalMap; + delete mOcclusionQuery; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -166,9 +164,13 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve void RenderingManager::update (float duration){ mActors.update (duration); - + + mOcclusionQuery->update(duration); + mSkyManager->update(duration); - + + mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); + mRendering.update(duration); mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index a4bef5384..01decf57c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -27,6 +27,7 @@ #include "player.hpp" #include "water.hpp" #include "localmap.hpp" +#include "occlusionquery.hpp" namespace Ogre { @@ -108,6 +109,9 @@ class RenderingManager: private RenderingInterface { void sunEnable(); void sunDisable(); + bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }; + OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }; + void setGlare(bool glare); void skyEnable (); void skyDisable (); @@ -145,10 +149,12 @@ class RenderingManager: private RenderingInterface { SkyManager* mSkyManager; - MWRender::Water *mWater; + OcclusionQuery* mOcclusionQuery; TerrainManager* mTerrainManager; + MWRender::Water *mWater; + OEngine::Render::OgreRenderer &mRendering; MWRender::Objects mObjects; @@ -164,7 +170,6 @@ class RenderingManager: private RenderingInterface { /// that the OGRE coordinate system matches that used internally in /// Morrowind. Ogre::SceneNode *mMwRoot; - Ogre::RaySceneQuery *mRaySceneQuery; OEngine::Physic::PhysicEngine* mPhysicsEngine; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index a41bc21e0..265008e34 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -12,6 +12,7 @@ #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" +#include "occlusionquery.hpp" using namespace MWRender; using namespace Ogre; @@ -30,7 +31,7 @@ BillboardObject::BillboardObject() void BillboardObject::setVisible(const bool visible) { - mNode->setVisible(visible); + mBBSet->setVisible(visible); } void BillboardObject::setSize(const float size) @@ -88,7 +89,7 @@ void BillboardObject::init(const String& textureName, /// \todo These billboards are not 100% correct, might want to revisit them later mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); - mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2); + mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2); mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); mBBSet->setCommonDirection( -position.normalisedCopy() ); mNode = rootNode->createChildSceneNode(); @@ -319,8 +320,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen , mThunderTextureUnit(NULL) , mRemainingTransitionTime(0.0f) , mGlareFade(0.0f) + , mGlare(0.0f) , mEnabled(true) - , mGlareEnabled(true) , mSunEnabled(true) , mMasserEnabled(true) , mSecundaEnabled(true) @@ -592,10 +593,23 @@ void SkyManager::update(float duration) mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); - // increase the strength of the sun glare effect depending - // on how directly the player is looking at the sun + if (mSunEnabled) { + // take 1/5 sec for fading the glare effect from invisible to full + if (mGlareFade > mGlare) + { + mGlareFade -= duration*5; + if (mGlareFade < mGlare) mGlareFade = mGlare; + } + else if (mGlareFade < mGlare) + { + mGlareFade += duration*5; + if (mGlareFade > mGlare) mGlareFade = mGlare; + } + + // increase the strength of the sun glare effect depending + // on how directly the player is looking at the sun Vector3 sun = mSunGlare->getPosition(); sun = Vector3(sun.x, sun.z, -sun.y); Vector3 cam = mViewport->getCamera()->getRealDirection(); @@ -603,21 +617,10 @@ void SkyManager::update(float duration) float val = 1- (angle.valueDegrees() / 180.f); val = (val*val*val*val)*2; - if (mGlareEnabled) - { - mGlareFade += duration*3; - if (mGlareFade > 1) mGlareFade = 1; - } - else - { - mGlareFade -= duration*3; - if (mGlareFade < 0.3) mGlareFade = 0; - } - - mSunGlare->setSize(val * (mGlareFade)); + mSunGlare->setSize(val * mGlareFade); } - mSunGlare->setVisible(mGlareFade>0 && mSunEnabled); + mSunGlare->setVisible(mSunEnabled); mSun->setVisible(mSunEnabled); mMasser->setVisible(mMasserEnabled); mSecunda->setVisible(mSecundaEnabled); @@ -719,15 +722,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) else strength = 1.f; - mSunGlare->setVisibility(weather.mGlareView * strength); - mSun->setVisibility(strength); + mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength); + mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0); mAtmosphereNight->setVisible(weather.mNight && mEnabled); } -void SkyManager::setGlare(bool glare) +void SkyManager::setGlare(const float glare) { - mGlareEnabled = glare; + mGlare = glare; } Vector3 SkyManager::getRealSunPos() @@ -812,3 +815,8 @@ void SkyManager::setDate(int day, int month) mDay = day; mMonth = month; } + +Ogre::SceneNode* SkyManager::getSunNode() +{ + return mSun->getNode(); +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index bf52afd8d..508af7673 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -109,58 +109,60 @@ namespace MWRender public: SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env); ~SkyManager(); - + void update(float duration); - + void enable(); - + void disable(); - + void setHour (double hour); ///< will be called even when sky is disabled. - + void setDate (int day, int month); ///< will be called even when sky is disabled. - + int getMasserPhase() const; ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, /// 3 waxing or waning gibbous, 4 full moon - + int getSecundaPhase() const; ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, /// 3 waxing or waning gibbous, 4 full moon - + void setMoonColour (bool red); ///< change Secunda colour to red - + void setCloudsOpacity(float opacity); ///< change opacity of the clouds - + void setWeather(const MWWorld::WeatherResult& weather); - + + Ogre::SceneNode* getSunNode(); + void sunEnable(); - + void sunDisable(); - + void setSunDirection(const Ogre::Vector3& direction); - + void setMasserDirection(const Ogre::Vector3& direction); - + void setSecundaDirection(const Ogre::Vector3& direction); - + void setMasserFade(const float fade); - + void setSecundaFade(const float fade); - + void masserEnable(); void masserDisable(); void secundaEnable(); void secundaDisable(); - + void setThunder(const float factor); - - void setGlare(bool glare); + + void setGlare(const float glare); Ogre::Vector3 getRealSunPos(); private: @@ -203,12 +205,12 @@ namespace MWRender float mRemainingTransitionTime; - float mGlareFade; + float mGlare; // target + float mGlareFade; // actual void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); bool mEnabled; - bool mGlareEnabled; bool mSunEnabled; bool mMasserEnabled; bool mSecundaEnabled; diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 798821d61..3cb316347 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -50,7 +50,7 @@ namespace Ogre terrain. @note Requires the Cg plugin to render correctly */ - class _OgreTerrainExport TerrainMaterialGeneratorB : public TerrainMaterialGenerator + class TerrainMaterialGeneratorB : public TerrainMaterialGenerator { public: TerrainMaterialGeneratorB(); @@ -58,7 +58,7 @@ namespace Ogre /** Shader model 2 profile target. */ - class _OgreTerrainExport SM2Profile : public TerrainMaterialGenerator::Profile + class SM2Profile : public TerrainMaterialGenerator::Profile { public: SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc); @@ -161,7 +161,7 @@ namespace Ogre void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt); /// Interface definition for helper class to generate shaders - class _OgreTerrainExport ShaderHelper : public TerrainAlloc + class ShaderHelper : public TerrainAlloc { public: ShaderHelper() {} @@ -194,7 +194,7 @@ namespace Ogre }; /// Utility class to help with generating shaders for Cg / HLSL. - class _OgreTerrainExport ShaderHelperCg : public ShaderHelper + class ShaderHelperCg : public ShaderHelper { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); @@ -212,7 +212,7 @@ namespace Ogre void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); }; - class _OgreTerrainExport ShaderHelperHLSL : public ShaderHelperCg + class ShaderHelperHLSL : public ShaderHelperCg { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); @@ -220,7 +220,7 @@ namespace Ogre }; /// Utility class to help with generating shaders for GLSL. - class _OgreTerrainExport ShaderHelperGLSL : public ShaderHelper + class ShaderHelperGLSL : public ShaderHelper { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index d5cc41b76..b4386a8a0 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -130,7 +130,7 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop); + context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWSound::Play_Loop : 0); } }; @@ -159,7 +159,7 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop); + context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0); } }; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 041759683..e57c1a709 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -25,14 +25,20 @@ static void throwALCerror(ALCdevice *device) { ALCenum err = alcGetError(device); if(err != ALC_NO_ERROR) - fail(alcGetString(device, err)); + { + const ALCchar *errstring = alcGetString(device, err); + fail(errstring ? errstring : ""); + } } static void throwALerror() { ALenum err = alGetError(); if(err != AL_NO_ERROR) - fail(alGetString(err)); + { + const ALchar *errstring = alGetString(err); + fail(errstring ? errstring : ""); + } } @@ -89,8 +95,7 @@ public: virtual void stop(); virtual bool isPlaying(); - virtual void setVolume(float volume); - virtual void update(const float *pos); + virtual void update(); void play(); bool process(); @@ -187,7 +192,6 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode } catch(std::exception &e) { - mOutput.mFreeSources.push_back(mSource); alDeleteBuffers(sNumBuffers, mBuffers); alGetError(); throw; @@ -255,16 +259,10 @@ bool OpenAL_SoundStream::isPlaying() return !mIsFinished; } -void OpenAL_SoundStream::setVolume(float volume) +void OpenAL_SoundStream::update() { - alSourcef(mSource, AL_GAIN, volume*mBaseVolume); - throwALerror(); - mVolume = volume; -} - -void OpenAL_SoundStream::update(const float *pos) -{ - alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]); + alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); @@ -321,15 +319,17 @@ bool OpenAL_SoundStream::process() } // -// A regular OpenAL sound +// A regular 2D OpenAL sound // class OpenAL_Sound : public Sound { +protected: OpenAL_Output &mOutput; ALuint mSource; ALuint mBuffer; +private: OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs); @@ -339,8 +339,23 @@ public: virtual void stop(); virtual bool isPlaying(); - virtual void setVolume(float volume); - virtual void update(const float *pos); + virtual void update(); +}; + +// +// A regular 3D OpenAL sound +// +class OpenAL_Sound3D : public OpenAL_Sound +{ + OpenAL_Sound3D(const OpenAL_Sound &rhs); + OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs); + +public: + OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf) + : OpenAL_Sound(output, src, buf) + { } + + virtual void update(); }; OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) @@ -372,16 +387,22 @@ bool OpenAL_Sound::isPlaying() return state==AL_PLAYING; } -void OpenAL_Sound::setVolume(float volume) +void OpenAL_Sound::update() { - alSourcef(mSource, AL_GAIN, volume*mBaseVolume); + alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); + alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); - mVolume = volume; } -void OpenAL_Sound::update(const float *pos) +void OpenAL_Sound3D::update() { - alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]); + if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) + alSourcef(mSource, AL_GAIN, 0.0f); + else + alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); @@ -410,8 +431,7 @@ std::vector OpenAL_Output::enumerate() void OpenAL_Output::init(const std::string &devname) { - if(mDevice || mContext) - fail("Device already open"); + deinit(); mDevice = alcOpenDevice(devname.c_str()); if(!mDevice) @@ -428,7 +448,12 @@ void OpenAL_Output::init(const std::string &devname) mContext = alcCreateContext(mDevice, NULL); if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE) + { + if(mContext) + alcDestroyContext(mContext); + mContext = 0; fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice))); + } alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); throwALerror(); @@ -442,33 +467,13 @@ void OpenAL_Output::init(const std::string &devname) { ALCuint maxtotal = std::min(maxmono+maxstereo, 256); if (maxtotal == 0) // workaround for broken implementations - { maxtotal = 256; - bool stop = false; - for(size_t i = 0;i < maxtotal && !stop;i++) // generate source until error returned - { - ALuint src = 0; - alGenSources(1, &src); - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - { - stop = true; - } - else - { - mFreeSources.push_back(src); - } - } - } - else // normal case + for(size_t i = 0;i < maxtotal;i++) { - for(size_t i = 0;i < maxtotal;i++) - { - ALuint src = 0; - alGenSources(1, &src); - throwALerror(); - mFreeSources.push_back(src); - } + ALuint src = 0; + alGenSources(1, &src); + throwALerror(); + mFreeSources.push_back(src); } } catch(std::exception &e) @@ -602,10 +607,8 @@ void OpenAL_Output::bufferFinished(ALuint buf) } -SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop) +SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) { - throwALerror(); - boost::shared_ptr sound; ALuint src=0, buf=0; @@ -640,7 +643,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float alSourcef(src, AL_PITCH, pitch); alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE)); + alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -650,11 +653,9 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float return sound; } -SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max, bool loop) +SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, + float min, float max, int flags) { - throwALerror(); - boost::shared_ptr sound; ALuint src=0, buf=0; @@ -666,7 +667,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, try { buf = getBuffer(fname); - sound.reset(new OpenAL_Sound(*this, src, buf)); + sound.reset(new OpenAL_Sound3D(*this, src, buf)); } catch(std::exception &e) { @@ -677,7 +678,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, throw; } - alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]); + alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y); alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); @@ -685,11 +686,12 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, alSourcef(src, AL_MAX_DISTANCE, max); alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - alSourcef(src, AL_GAIN, volume); + alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ? + 0.0f : volume); alSourcef(src, AL_PITCH, pitch); alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE)); + alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -702,8 +704,6 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch) { - throwALerror(); - boost::shared_ptr sound; ALuint src; @@ -743,61 +743,21 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa return sound; } -SoundPtr OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max) + +void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir) { - throwALerror(); + mPos = pos; - boost::shared_ptr sound; - ALuint src; - - if(mFreeSources.empty()) - fail("No free sources"); - src = mFreeSources.front(); - mFreeSources.pop_front(); - - try + if(mContext) { - DecoderPtr decoder = mManager.getDecoder(); - decoder->open(fname); - sound.reset(new OpenAL_SoundStream(*this, src, decoder)); + ALfloat orient[6] = { + atdir.x, atdir.z, -atdir.y, + updir.x, updir.z, -updir.y + }; + alListener3f(AL_POSITION, mPos.x, mPos.z, -mPos.y); + alListenerfv(AL_ORIENTATION, orient); + throwALerror(); } - catch(std::exception &e) - { - mFreeSources.push_back(src); - throw; - } - - alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, min); - alSourcef(src, AL_MAX_DISTANCE, max); - alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - - alSourcef(src, AL_GAIN, volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, AL_FALSE); - throwALerror(); - - sound->play(); - return sound; -} - - -void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir) -{ - float orient[6] = { - atdir[0], atdir[2], -atdir[1], - updir[0], updir[2], -updir[1] - }; - - alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]); - alListenerfv(AL_ORIENTATION, orient); - throwALerror(); } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d288a62f3..a709576ba 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -40,15 +40,12 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop); - virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max, bool loop); - + virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); + virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + float volume, float pitch, float min, float max, int flags); virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch); - virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max); - virtual void updateListener(const float *pos, const float *atdir, const float *updir); + virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir); OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); @@ -60,6 +57,7 @@ namespace MWSound std::auto_ptr mStreamThread; friend class OpenAL_Sound; + friend class OpenAL_Sound3D; friend class OpenAL_SoundStream; friend class SoundManager; }; diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 2cbd48d96..ca12ec557 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -1,30 +1,37 @@ #ifndef GAME_SOUND_SOUND_H #define GAME_SOUND_SOUND_H +#include + namespace MWSound { class Sound { - virtual void update(const float *pos) = 0; + virtual void update() = 0; Sound& operator=(const Sound &rhs); Sound(const Sound &rhs); protected: + Ogre::Vector3 mPos; float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */ float mBaseVolume; float mMinDistance; float mMaxDistance; + int mFlags; public: virtual void stop() = 0; virtual bool isPlaying() = 0; - virtual void setVolume(float volume) = 0; + void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } + void setVolume(float volume) { mVolume = volume; } - Sound() : mVolume(1.0f) + Sound() : mPos(0.0f, 0.0f, 0.0f) + , mVolume(1.0f) , mBaseVolume(1.0f) , mMinDistance(20.0f) /* 1 * min_range_scale */ , mMaxDistance(12750.0f) /* 255 * max_range_scale */ + , mFlags(Play_Normal) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 794383591..1507e1847 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -4,6 +4,8 @@ #include #include +#include + #include "soundmanager.hpp" #include "../mwworld/ptr.hpp" @@ -22,19 +24,23 @@ namespace MWSound virtual void init(const std::string &devname="") = 0; virtual void deinit() = 0; - virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop) = 0; - virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max, bool loop) = 0; + virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + float volume, float pitch, float min, float max, int flags) = 0; virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch) = 0; - virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch, - float min, float max) = 0; - virtual void updateListener(const float *pos, const float *atdir, const float *updir) = 0; + virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir) = 0; Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); - Sound_Output(SoundManager &mgr) : mManager(mgr) { } + protected: + Ogre::Vector3 mPos; + + Sound_Output(SoundManager &mgr) + : mManager(mgr) + , mPos(0.0f, 0.0f, 0.0f) + { } public: virtual ~Sound_Output() { } diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index ad9e47f72..2c2e6e9f9 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -41,6 +41,8 @@ namespace MWSound SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment) : mResourceMgr(Ogre::ResourceGroupManager::getSingleton()) , mEnvironment(environment) + , mOutput(new DEFAULT_OUTPUT(*this)) + { if(!useSound) return; @@ -50,8 +52,6 @@ namespace MWSound try { - mOutput.reset(new DEFAULT_OUTPUT(*this)); - std::vector names = mOutput->enumerate(); std::cout <<"Enumerated output devices:"<< std::endl; for(size_t i = 0;i < names.size();i++) @@ -62,8 +62,6 @@ namespace MWSound catch(std::exception &e) { std::cout <<"Sound init failed: "<sound; + return "Sound/"+snd->sound; } @@ -182,11 +180,13 @@ namespace MWSound { // The range values are not tested float basevol = 1.0f; /* TODO: volume settings */ - std::string filePath = std::string("Sound/")+filename; + std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getCellRef().pos; + const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - SoundPtr sound = mOutput->playSound3D(filePath, pos.pos, basevol, 1.0f, - 20.0f, 12750.0f, false); + SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, + 20.0f, 12750.0f, Play_Normal); + sound->mPos = objpos; sound->mBaseVolume = basevol; mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); @@ -203,7 +203,7 @@ namespace MWSound } - SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop) + SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) { SoundPtr sound; try @@ -212,11 +212,12 @@ namespace MWSound float min, max; std::string file = lookup(soundId, basevol, min, max); - sound = mOutput->playSound(file, volume*basevol, pitch, loop); + sound = mOutput->playSound(file, volume*basevol, pitch, mode); sound->mVolume = volume; sound->mBaseVolume = basevol; sound->mMinDistance = min; sound->mMaxDistance = max; + sound->mFlags = mode; mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } @@ -228,8 +229,7 @@ namespace MWSound } SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, bool loop, - bool untracked) + float volume, float pitch, int mode) { SoundPtr sound; try @@ -239,15 +239,20 @@ namespace MWSound float min, max; std::string file = lookup(soundId, basevol, min, max); const ESM::Position &pos = ptr.getCellRef().pos; + const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, pos.pos, volume*basevol, pitch, min, max, loop); + sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); + sound->mPos = objpos; sound->mVolume = volume; sound->mBaseVolume = basevol; sound->mMinDistance = min; sound->mMaxDistance = max; + sound->mFlags = mode; - mActiveSounds[sound] = (!untracked ? std::make_pair(ptr, soundId) : - std::make_pair(MWWorld::Ptr(), soundId)); + if((mode&Play_NoTrack)) + mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + else + mActiveSounds[sound] = std::make_pair(ptr, soundId); } catch(std::exception &e) { @@ -326,11 +331,12 @@ namespace MWSound void SoundManager::updateObject(MWWorld::Ptr ptr) { const ESM::Position &pos = ptr.getCellRef().pos; + const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { if(snditer->second.first == ptr) - snditer->first->update(pos.pos); + snditer->first->setPosition(objpos); snditer++; } } @@ -411,9 +417,9 @@ namespace MWSound // The output handler is expecting vectors oriented like the game // (that is, -Z goes down, +Y goes forward), but that's not what we // get from Ogre's camera, so we have to convert. - float pos[3] = { nPos[0], -nPos[2], nPos[1] }; - float at[3] = { nDir[0], -nDir[2], nDir[1] }; - float up[3] = { nUp[0], -nUp[2], nUp[1] }; + const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]); + const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]); + const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]); mOutput->updateListener(pos, at, up); // Check if any sounds are finished playing, and trash them @@ -423,7 +429,10 @@ namespace MWSound if(!snditer->first->isPlaying()) mActiveSounds.erase(snditer++); else + { + snditer->first->update(); snditer++; + } } } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 3ab1e881c..de5cca839 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -30,6 +30,19 @@ namespace MWSound typedef boost::shared_ptr DecoderPtr; typedef boost::shared_ptr SoundPtr; + enum PlayMode { + Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ + Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ + Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position + * but do not keep it updated (the sound will not move with + * the object and will not stop when the object is deleted. */ + }; + static inline int operator|(const PlayMode &a, const PlayMode &b) + { return (int)a | (int)b; } + static inline int operator&(const PlayMode &a, const PlayMode &b) + { return (int)a & (int)b; } + class SoundManager { Ogre::ResourceGroupManager& mResourceMgr; @@ -87,12 +100,11 @@ namespace MWSound bool sayDone(MWWorld::Ptr reference) const; ///< Is actor not speaking? - SoundPtr playSound(const std::string& soundId, float volume, float pitch, bool loop=false); + SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound, independently of 3D-position SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, bool loop, - bool untracked=false); + float volume, float pitch, int mode=Play_Normal); ///< Play a sound from an object void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index bb2f9f8a9..83c3ef2ba 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -50,6 +50,28 @@ namespace MWWorld return mEngine->rayTest(from,to); } + + std::vector < std::pair > PhysicsSystem::getFacedObjects () + { + //get a ray pointing to the center of the viewport + Ray centerRay = mRender.getCamera()->getCameraToViewportRay( + mRender.getViewport()->getWidth()/2, + mRender.getViewport()->getHeight()/2); + btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y); + btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y); + + return mEngine->rayTest2(from,to); + } + + btVector3 PhysicsSystem::getRayPoint(float extent) + { + //get a ray pointing to the center of the viewport + Ray centerRay = mRender.getCamera()->getCameraToViewportRay( + mRender.getViewport()->getWidth()/2, + mRender.getViewport()->getHeight()/2); + btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); + return result; + } bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) { diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 78cbde083..7b2d77325 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -35,7 +35,11 @@ namespace MWWorld bool toggleCollisionMode(); std::pair getFacedHandle (MWWorld::World& world); - + + btVector3 getRayPoint(float extent); + + std::vector < std::pair > getFacedObjects (); + // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index e6d44dd8c..6f03fa37f 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -157,7 +157,8 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment, const std::string& encoding) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this) + mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this), + mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -498,13 +499,21 @@ namespace MWWorld std::string World::getFacedHandle() { - std::pair result = mPhysics->getFacedHandle (*this); + if (!mRendering->occlusionQuerySupported()) + { + std::pair result = mPhysics->getFacedHandle (*this); - if (result.first.empty() || - result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) - return ""; + if (result.first.empty() || + result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) + return ""; - return result.first; + return result.first; + } + else + { + // updated every few frames in update() + return mFacedHandle; + } } void World::deleteObject (Ptr ptr) @@ -706,13 +715,82 @@ namespace MWWorld mWeatherManager->update (duration); - // cast a ray from player to sun to detect if the sun is visible - // this is temporary until we find a better place to put this code - // currently its here because we need to access the physics system - float* p = mPlayer->getPlayer().getRefData().getPosition().pos; - Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); - sun = Vector3(sun.x, -sun.z, sun.y); - mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); + if (!mRendering->occlusionQuerySupported()) + { + // cast a ray from player to sun to detect if the sun is visible + // this is temporary until we find a better place to put this code + // currently its here because we need to access the physics system + float* p = mPlayer->getPlayer().getRefData().getPosition().pos; + Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); + sun = Vector3(sun.x, -sun.z, sun.y); + mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); + } + + // update faced handle (object the player is looking at) + // this uses a mixture of raycasts and occlusion queries. + else // if (mRendering->occlusionQuerySupported()) + { + MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery(); + if (!query->occlusionTestPending()) + { + // get result of last query + if (mNumFacing == 0) mFacedHandle = ""; + else if (mNumFacing == 1) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced1Name : ""; + } + else if (mNumFacing == 2) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced2Name : mFaced1Name; + } + + // send new query + // figure out which object we want to test against + std::vector < std::pair < float, std::string > > results = mPhysics->getFacedObjects(); + + // ignore the player + for (std::vector < std::pair < float, std::string > >::iterator it = results.begin(); + it != results.end(); ++it) + { + if ( (*it).second == mPlayer->getPlayer().getRefData().getHandle() ) + { + results.erase(it); + break; + } + } + + if (results.size() == 0) + { + mNumFacing = 0; + } + else if (results.size() == 1) + { + mFaced1 = getPtrViaHandle(results.front().second); + mFaced1Name = results.front().second; + mNumFacing = 1; + + btVector3 p = mPhysics->getRayPoint(results.front().first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); + query->occlusionTest(pos, node); + } + else + { + mFaced1Name = results.front().second; + mFaced2Name = results[1].second; + mFaced1 = getPtrViaHandle(results.front().second); + mFaced2 = getPtrViaHandle(results[1].second); + mNumFacing = 2; + + btVector3 p = mPhysics->getRayPoint(results[1].first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node = mFaced2.getRefData().getBaseNode(); + query->occlusionTest(pos, node); + } + } + } } bool World::isCellExterior() const diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index bfc33ae3d..7f8b7e861 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -93,6 +93,12 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); + std::string mFacedHandle; + Ptr mFaced1; + Ptr mFaced2; + std::string mFaced1Name; + std::string mFaced2Name; + int mNumFacing; int getDaysPerMonth (int month) const; diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 80d92dd52..f4f4b898c 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -45,6 +45,20 @@ struct ciLessBoost : std::binary_function } }; +struct pathComparer +{ +private: + int m_start, m_size; + +public: + pathComparer(int start, int size) : m_start(start), m_size(size) { } + + bool operator() (const std::string& first, const std::string& other) + { + return lexicographical_compare(first.substr(m_start,m_size), other.substr(m_start,m_size), boost::algorithm::is_iless()); + } +}; + static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive @@ -55,16 +69,62 @@ class DirArchive: public Ogre::FileSystemArchive std::map, ciLessBoost> m; unsigned int cutoff; - bool comparePortion(std::string file1, std::string file2, int start, int size) const + bool findFile(const String& filename, std::string& copy) const { - for(int i = start; i < start+size; i++) + if (filename.find(".tga") != std::string::npos) + return false; + { - char one = file1.at(i); - char two = file2.at(i); - if(tolower(one) != tolower(two) ) - return false; + String passed = filename; + if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' + || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' + || filename.at(filename.length() - 1) == '|') + { + passed = filename.substr(0, filename.length() - 2); + } + if(filename.at(filename.length() - 2) == '>') + passed = filename.substr(0, filename.length() - 6); + copy = passed; } - return true; + + std::replace(copy.begin(), copy.end(), '\\', '/'); + + if(copy.at(0) == '/') + copy.erase(0, 1); + + if(fsstrict == true) + return true; + + std::string folder; + int delimiter = 0; + size_t lastSlash = copy.rfind('/'); + if (lastSlash != std::string::npos) + { + folder = copy.substr(0, lastSlash); + delimiter = lastSlash+1; + } + + std::vector current; + { + std::map,ciLessBoost>::const_iterator found = m.find(folder); + + if (found == m.end()) + { + return false; + } + else + current = found->second; + } + + pathComparer comp(delimiter, copy.size() - delimiter-1); + std::vector::iterator find = std::lower_bound(current.begin(), current.end(), copy, comp); + if (find != current.end() && !comp(copy, current.front())) + { + copy = *find; + return true; + } + + return false; } public: @@ -83,16 +143,14 @@ class DirArchive: public Ogre::FileSystemArchive //need to cut off first boost::filesystem::directory_iterator dir_iter(d), dir_end; std::vector filesind; - boost::filesystem::path f; for(;dir_iter != dir_end; dir_iter++) { if(boost::filesystem::is_directory(*dir_iter)) populateMap(*dir_iter); else { - - f = *dir_iter; - std::string s = f.string(); + std::string s = dir_iter->path().string(); + std::replace(s.begin(), s.end(), '\\', '/'); std::string small; if(cutoff < s.size()) @@ -103,14 +161,17 @@ class DirArchive: public Ogre::FileSystemArchive filesind.push_back(small); } } + std::sort(filesind.begin(), filesind.end(), ciLessBoost()); + std::string small; std::string original = d.string(); + std::replace(original.begin(), original.end(), '\\', '/'); if(cutoff < original.size()) small = original.substr(cutoff, original.size() - cutoff); else small = original.substr(cutoff - 1, original.size() - cutoff); - m[small] = filesind; + m[small] = filesind; } bool isCaseSensitive() const { return fsstrict; } @@ -120,97 +181,21 @@ class DirArchive: public Ogre::FileSystemArchive void unload() {} bool exists(const String& filename) { - std::string copy = filename; + std::string copy; - - - for (unsigned int i = 0; i < filename.size(); i++) - { - if(copy.at(i) == '\\' ){ - copy.replace(i, 1, "/"); - } - } - - - if(copy.at(0) == '\\' || copy.at(0) == '/') - { - copy.erase(0, 1); - } - if(fsstrict == true) - { - //std::cout << "fsstrict " << copy << "\n"; + if (findFile(filename, copy)) return FileSystemArchive::exists(copy); - } - - - int last = copy.size() - 1; - int i = last; - - for (;last >= 0; i--) - { - if(copy.at(i) == '/' || copy.at(i) == '\\') - break; - } - - std::string folder = copy.substr(0, i); //folder with no slash - - std::vector& current = m[folder]; - - for(std::vector::iterator iter = current.begin(); iter != current.end(); iter++) - { - if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){ - return FileSystemArchive::exists(*iter); - } - } - return false; } DataStreamPtr open(const String& filename, bool readonly = true) const { - std::map, ciLessBoost> mlocal = m; - std::string copy = filename; + std::string copy; - - - for (unsigned int i = 0; i < filename.size(); i++) - { - if(copy.at(i) == '\\' ){ - copy.replace(i, 1, "/"); - } - } - - - if(copy.at(0) == '\\' || copy.at(0) == '/') - { - copy.erase(0, 1); - } - - if(fsstrict == true) - { + if (findFile(filename, copy)) return FileSystemArchive::open(copy, readonly); - } - - int last = copy.size() - 1; - int i = last; - - for (;last >= 0; i--) - { - if(copy.at(i) == '/' || copy.at(i) == '\\') - break; - } - - std::string folder = copy.substr(0, i); //folder with no slash - std::vector current = mlocal[folder]; - - for(std::vector::iterator iter = current.begin(); iter != current.end(); iter++) - { - if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){ - return FileSystemArchive::open(*iter, readonly); - } - } DataStreamPtr p; return p; } @@ -261,7 +246,7 @@ bool exists(const String& filename) { } // Check if the file exists. - bool cexists(const String& filename) const { + bool cexists(const String& filename) const { String passed = filename; if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' @@ -272,7 +257,7 @@ bool exists(const String& filename) { if(filename.at(filename.length() - 2) == '>') passed = filename.substr(0, filename.length() - 6); -return arc.exists(passed.c_str()); +return arc.exists(passed.c_str()); } time_t getModifiedTime(const String&) { return 0; } diff --git a/components/files/collections.cpp b/components/files/collections.cpp index 424b558e6..50340dca4 100644 --- a/components/files/collections.cpp +++ b/components/files/collections.cpp @@ -30,4 +30,9 @@ namespace Files return iter->second; } + + const Files::PathContainer& Collections::getPaths() const + { + return mDirectories; + } } diff --git a/components/files/collections.hpp b/components/files/collections.hpp index 1ddca9a5b..70aaec55e 100644 --- a/components/files/collections.hpp +++ b/components/files/collections.hpp @@ -21,6 +21,8 @@ namespace Files /// leading dot and must be all lower-case. const MultiDirCollection& getCollection(const std::string& extension) const; + const Files::PathContainer& getPaths() const; + private: typedef std::map MultiDirCollectionContainer; Files::PathContainer mDirectories; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index f943231d0..2ab6ae621 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1368,7 +1368,7 @@ void NIFLoader::loadResource(Resource *resource) if (!vfs->isFile(resourceName)) { - warn("File not found."); + warn("File "+resourceName+" not found."); return; } diff --git a/files/mac/openmw.icns b/files/mac/openmw.icns index dfea24660..3ff899a79 100644 Binary files a/files/mac/openmw.icns and b/files/mac/openmw.icns differ diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 8b9f3dfec..e7da9f085 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -151,7 +151,8 @@ namespace Physic - PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) + PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) : + mDebugActive(0) { // Set up the collision configuration and dispatcher collisionConfiguration = new btDefaultCollisionConfiguration(); @@ -203,6 +204,13 @@ namespace Physic createDebugRendering(); } mDebugDrawer->setDebugMode(mode); + mDebugActive = mode; + } + + bool PhysicEngine::toggleDebugRendering() + { + setDebugRenderingMode(!mDebugActive); + return mDebugActive; } PhysicEngine::~PhysicEngine() @@ -418,4 +426,35 @@ namespace Physic return std::pair(name,d); } + + std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) + { + MyRayResultCallback resultCallback1; + resultCallback1.m_collisionFilterMask = COL_WORLD; + dynamicsWorld->rayTest(from, to, resultCallback1); + std::vector< std::pair > results = resultCallback1.results; + + MyRayResultCallback resultCallback2; + resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL; + dynamicsWorld->rayTest(from, to, resultCallback2); + std::vector< std::pair > actorResults = resultCallback2.results; + + std::vector< std::pair > results2; + + for (std::vector< std::pair >::iterator it=results.begin(); + it != results.end(); ++it) + { + results2.push_back( std::make_pair( (*it).first, static_cast(*(*it).second).mName ) ); + } + + for (std::vector< std::pair >::iterator it=actorResults.begin(); + it != actorResults.end(); ++it) + { + results2.push_back( std::make_pair( (*it).first, static_cast(*(*it).second).mName ) ); + } + + std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp); + + return results2; + } }}; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 57ffe9130..8d177efda 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -199,11 +199,18 @@ namespace Physic */ void setDebugRenderingMode(int mode); + bool toggleDebugRendering(); + /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). */ std::pair rayTest(btVector3& from,btVector3& to); + /** + * Return all objects hit by a ray. + */ + std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + //event list of non player object std::list NPEventList; @@ -230,6 +237,26 @@ namespace Physic //debug rendering BtOgre::DebugDrawer* mDebugDrawer; bool isDebugCreated; + bool mDebugActive; + }; + + + struct MyRayResultCallback : public btCollisionWorld::RayResultCallback + { + virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool bNormalInWorldSpace) + { + results.push_back( std::make_pair(rayResult.m_hitFraction, rayResult.m_collisionObject) ); + return rayResult.m_hitFraction; + } + + static bool cmp( const std::pair& i, const std::pair& j ) + { + if( i.first > j.first ) return false; + if( j.first > i.first ) return true; + return false; + } + + std::vector < std::pair > results; }; }}