diff --git a/CMakeLists.txt b/CMakeLists.txt index ebb3e7675..cc85da9ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) -endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) +endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) # Macros include(OpenMWMacros) @@ -165,10 +165,10 @@ else() endif() IF(BUILD_OPENMW OR BUILD_OPENCS) - # Sound setup - find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE) - # Required for building the FFmpeg headers - add_definitions(-D__STDC_CONSTANT_MACROS) + # Sound setup + find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE) + # Required for building the FFmpeg headers + add_definitions(-D__STDC_CONSTANT_MACROS) # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) @@ -252,10 +252,15 @@ IF(BUILD_OPENMW OR BUILD_OPENCS) endif() endif() + set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape + if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR}) + set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine + endif() + find_package(MyGUI 3.2.1 REQUIRED) find_package(SDL2 REQUIRED) find_package(OpenAL REQUIRED) - find_package(Bullet 283 REQUIRED COMPONENTS BulletCollision LinearMath) + find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) ENDIF(BUILD_OPENMW OR BUILD_OPENCS) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3725c0f6d..fb928e228 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -675,8 +675,6 @@ void OMW::Engine::go() } else if (!mSkipMenu) { - mEnvironment.getWorld()->preloadCommonAssets(); - // start in main menu mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); try diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 37ea1524e..27f59f437 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -95,8 +95,6 @@ namespace MWBase virtual ~World() {} - virtual void preloadCommonAssets() = 0; - virtual void startNewGame (bool bypass) = 0; ///< \param bypass Bypass regular game start. diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 0ab636865..6d7daa3b8 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -268,12 +268,12 @@ namespace MWPhysics ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35 || !isWalkableSlope(tracer.mPlaneNormal))) { - actor->setOnSlope(isWalkableSlope(resultCallback1.m_hitNormalWorld)); + actor->setOnSlope(!isWalkableSlope(resultCallback1.m_hitNormalWorld)); return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f); } else { - actor->setOnSlope(isWalkableSlope(tracer.mPlaneNormal)); + actor->setOnSlope(!isWalkableSlope(tracer.mPlaneNormal)); } return tracer.mEndPos; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 976d83a8f..0865850e2 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -543,7 +543,7 @@ namespace MWRender size_t blendMask = detectBlendMask(node); // clone the controller, because each Animation needs its own ControllerSource - osg::ref_ptr cloned = osg::clone(it->second.get(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr cloned = new NifOsg::KeyframeController(*it->second, osg::CopyOp::SHALLOW_COPY); cloned->setSource(mAnimationTimePtr[blendMask]); animsrc->mControllerMap[blendMask].insert(std::make_pair(bonename, cloned)); @@ -1272,7 +1272,7 @@ namespace MWRender writableStateSet = node->getOrCreateStateSet(); else { - writableStateSet = osg::clone(node->getStateSet(), osg::CopyOp::SHALLOW_COPY); + writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY); node->setStateSet(writableStateSet); } writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 3b45a3c60..240ddc2e5 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -231,13 +231,51 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int segment.mMapTexture = texture; } +bool needUpdate(std::set >& renderedGrid, std::set >& currentGrid, int cellX, int cellY) +{ + // if all the cells of the current grid are contained in the rendered grid then we can keep the old render + for (int dx=-1;dx<2;dx+=1) + { + for (int dy=-1;dy<2;dy+=1) + { + bool haveInRenderedGrid = renderedGrid.find(std::make_pair(cellX+dx,cellY+dy)) != renderedGrid.end(); + bool haveInCurrentGrid = currentGrid.find(std::make_pair(cellX+dx,cellY+dy)) != currentGrid.end(); + if (haveInCurrentGrid && !haveInRenderedGrid) + return true; + } + } + return false; +} + void LocalMap::requestMap(std::set cells) { + std::set > grid; + for (std::set::iterator it = cells.begin(); it != cells.end(); ++it) + { + const MWWorld::CellStore* cell = *it; + if (cell->isExterior()) + grid.insert(std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())); + } + for (std::set::iterator it = cells.begin(); it != cells.end(); ++it) { const MWWorld::CellStore* cell = *it; if (cell->isExterior()) - requestExteriorMap(cell); + { + int cellX = cell->getCell()->getGridX(); + int cellY = cell->getCell()->getGridY(); + + MapSegment& segment = mSegments[std::make_pair(cellX, cellY)]; + if (!needUpdate(segment.mGrid, grid, cellX, cellY)) + { + continue; + } + else + { + segment.mGrid = grid; + requestExteriorMap(cell); + } + } else requestInteriorMap(cell); } diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 1928871ca..febe79d84 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -124,6 +124,8 @@ namespace MWRender osg::ref_ptr mFogOfWarTexture; osg::ref_ptr mFogOfWarImage; + std::set > mGrid; // the grid that was active at the time of rendering this segment + bool mHasFogState; }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7bb72ae5d..7f5cd23c7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -140,14 +141,24 @@ namespace MWRender virtual void doWork() { - for (std::vector::const_iterator it = mModels.begin(); it != mModels.end(); ++it) - mResourceSystem->getSceneManager()->getTemplate(*it); - for (std::vector::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it) - mResourceSystem->getImageManager()->getImage(*it); + try + { + for (std::vector::const_iterator it = mModels.begin(); it != mModels.end(); ++it) + mResourceSystem->getSceneManager()->cacheInstance(*it); + for (std::vector::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it) + mResourceSystem->getImageManager()->getImage(*it); + for (std::vector::const_iterator it = mKeyframes.begin(); it != mKeyframes.end(); ++it) + mResourceSystem->getKeyframeManager()->get(*it); + } + catch (std::exception&) + { + // ignore error (will be shown when these are needed proper) + } } std::vector mModels; std::vector mTextures; + std::vector mKeyframes; private: Resource::ResourceSystem* mResourceSystem; @@ -308,6 +319,13 @@ namespace MWRender mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures); mWater->listAssetsToPreload(workItem->mTextures); + const char* basemodels[] = {"xbase_anim", "xbase_anim.1st", "xbase_anim_female", "xbase_animkna"}; + for (size_t i=0; imModels.push_back(std::string("meshes/") + basemodels[i] + ".nif"); + workItem->mKeyframes.push_back(std::string("meshes/") + basemodels[i] + ".kf"); + } + workItem->mTextures.push_back("textures/_land_default.dds"); mWorkQueue->addWorkItem(workItem); @@ -435,14 +453,6 @@ namespace MWRender mViewer->getCamera()->setCullMask(mask); return enabled; } - /* - else //if (mode == Render_BoundingBoxes) - { - bool show = !mRendering.getScene()->getShowBoundingBoxes(); - mRendering.getScene()->showBoundingBoxes(show); - return show; - } - */ return false; } diff --git a/apps/openmw/mwrender/rendermode.hpp b/apps/openmw/mwrender/rendermode.hpp index ba767bc55..2847888d1 100644 --- a/apps/openmw/mwrender/rendermode.hpp +++ b/apps/openmw/mwrender/rendermode.hpp @@ -9,7 +9,6 @@ namespace MWRender Render_CollisionDebug, Render_Wireframe, Render_Pathgrid, - Render_BoundingBoxes, Render_Water, Render_Scene }; diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index aa8aaccbc..74047b58b 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -55,7 +55,7 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou osg::ref_ptr stateset; if (node->getStateSet()) - stateset = osg::clone(node->getStateSet(), osg::CopyOp::SHALLOW_COPY); + stateset = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY); else stateset = new osg::StateSet; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index e5a991722..197b5881e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -278,10 +278,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { bool enabled = - MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_BoundingBoxes); + MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug); runtime.getContext().report (enabled ? - "Bounding Box Rendering -> On" : "Bounding Box Rendering -> Off"); + "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); } }; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 651982344..038b2338b 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -242,6 +242,10 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception&) { + if(mStream) + avcodec_close((*mStream)->codec); + mStream = NULL; + if (mFormatCtx->pb->buffer != NULL) { av_free(mFormatCtx->pb->buffer); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index f767c6254..7b90da43b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -55,6 +55,7 @@ namespace MWWorld , mKeyframeManager(keyframeManager) , mTerrain(terrain) , mPreloadInstances(preloadInstances) + , mAbort(false) { ListModelsVisitor visitor (mMeshes); if (cell->getState() == MWWorld::CellStore::State_Loaded) @@ -76,11 +77,30 @@ namespace MWWorld } } + virtual void abort() + { + mAbort = true; + } + /// Preload work to be called from the worker thread. virtual void doWork() { + if (mIsExterior) + { + try + { + mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); + } + catch(std::exception& e) + { + } + } + for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) { + if (mAbort) + break; + try { std::string mesh = *it; @@ -119,17 +139,6 @@ namespace MWWorld // error will be shown when visiting the cell } } - - if (mIsExterior) - { - try - { - mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); - } - catch(std::exception& e) - { - } - } } private: @@ -144,6 +153,8 @@ namespace MWWorld Terrain::World* mTerrain; bool mPreloadInstances; + volatile bool mAbort; + // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state std::vector > mPreloadedObjects; }; @@ -187,7 +198,10 @@ namespace MWWorld CellPreloader::~CellPreloader() { for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) + { + it->second.mWorkItem->abort(); it->second.mWorkItem->waitTillDone(); + } mPreloadCells.clear(); } @@ -228,7 +242,10 @@ namespace MWWorld } if (oldestTimestamp + threshold < timestamp) + { + oldestCell->second.mWorkItem->abort(); mPreloadCells.erase(oldestCell); + } else return; } @@ -246,18 +263,42 @@ namespace MWWorld { // do the deletion in the background thread if (found->second.mWorkItem) + { + found->second.mWorkItem->abort(); mUnrefQueue->push(mPreloadCells[cell].mWorkItem); + } mPreloadCells.erase(found); } } + void CellPreloader::clear() + { + for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) + { + if (it->second.mWorkItem) + { + it->second.mWorkItem->abort(); + mUnrefQueue->push(it->second.mWorkItem); + } + + mPreloadCells.erase(it++); + } + } + void CellPreloader::updateCache(double timestamp) { for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) { if (mPreloadCells.size() >= mMinCacheSize && it->second.mTimeStamp < timestamp - mExpiryDelay) + { + if (it->second.mWorkItem) + { + it->second.mWorkItem->abort(); + mUnrefQueue->push(it->second.mWorkItem); + } mPreloadCells.erase(it++); + } else ++it; } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 3836b6869..e8e59a3a2 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -37,6 +37,8 @@ namespace MWWorld void notifyLoaded(MWWorld::CellStore* cell); + void clear(); + /// Removes preloaded cells that have not had a preload request for a while. void updateCache(double timestamp); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4d1c5a59f..85e6d87d0 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -205,9 +205,9 @@ namespace MWWorld if (mPreloadEnabled) { mPreloadTimer += duration; - if (mPreloadTimer > 0.25f) + if (mPreloadTimer > 0.1f) { - preloadCells(); + preloadCells(0.1f); mPreloadTimer = 0.f; } } @@ -328,13 +328,15 @@ namespace MWWorld mPreloader->notifyLoaded(cell); } - void Scene::changeToVoid() + void Scene::clear() { CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) unloadCell (active++); assert(mActiveCells.empty()); mCurrentCell = NULL; + + mPreloader->clear(); } void Scene::playerMoved(const osg::Vec3f &pos) @@ -481,6 +483,8 @@ namespace MWWorld mechMgr->watchActor(player); MWBase::Environment::get().getWorld()->adjustSky(); + + mLastPlayerPos = pos.asVec3(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) @@ -675,17 +679,24 @@ namespace MWWorld return Ptr(); } - void Scene::preloadCells() + void Scene::preloadCells(float dt) { + const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + osg::Vec3f moved = playerPos - mLastPlayerPos; + osg::Vec3f predictedPos = playerPos + moved / dt; + + mLastPlayerPos = playerPos; + if (mPreloadDoors) - preloadTeleportDoorDestinations(); + preloadTeleportDoorDestinations(playerPos, predictedPos); if (mPreloadExteriorGrid) - preloadExteriorGrid(); + preloadExteriorGrid(playerPos, predictedPos); if (mPreloadFastTravel) - preloadFastTravelDestinations(); + preloadFastTravelDestinations(playerPos, predictedPos); } - void Scene::preloadTeleportDoorDestinations() + void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos) { std::vector teleportDoors; for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); @@ -703,11 +714,11 @@ namespace MWWorld } } - const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); for (std::vector::iterator it = teleportDoors.begin(); it != teleportDoors.end(); ++it) { const MWWorld::ConstPtr& door = *it; - float sqrDistToPlayer = (player.getRefData().getPosition().asVec3() - door.getRefData().getPosition().asVec3()).length2(); + float sqrDistToPlayer = (playerPos - door.getRefData().getPosition().asVec3()).length2(); + sqrDistToPlayer = std::min(sqrDistToPlayer, (predictedPos - door.getRefData().getPosition().asVec3()).length2()); if (sqrDistToPlayer < mPreloadDistance*mPreloadDistance) { @@ -730,15 +741,13 @@ namespace MWWorld } } - void Scene::preloadExteriorGrid() + void Scene::preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos) { if (!MWBase::Environment::get().getWorld()->isCellExterior()) return; int halfGridSizePlusOne = mHalfGridSize + 1; - const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); int cellX,cellY; getGridCenter(cellX,cellY); @@ -757,6 +766,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->indexToPosition(cellX+dx, cellY+dy, thisCellCenterX, thisCellCenterY, true); float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); + dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y()))); float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; if (dist < loadDist) @@ -816,7 +826,7 @@ namespace MWWorld std::vector mList; }; - void Scene::preloadFastTravelDestinations() + void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/) // ignore predictedPos here since opening dialogue with travel service takes extra time { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 33886eed4..328538076 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -68,6 +68,8 @@ namespace MWWorld bool mPreloadDoors; bool mPreloadFastTravel; + osg::Vec3f mLastPlayerPos; + void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center @@ -75,12 +77,10 @@ namespace MWWorld void getGridCenter(int& cellX, int& cellY); - void preloadCells(); - void preloadTeleportDoorDestinations(); - void preloadExteriorGrid(); - void preloadFastTravelDestinations(); - - void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); + void preloadCells(float dt); + void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); + void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); + void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); public: @@ -88,6 +88,8 @@ namespace MWWorld ~Scene(); + void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); + void unloadCell (CellStoreCollection::iterator iter); void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn); @@ -111,7 +113,7 @@ namespace MWWorld ///< Move to exterior cell. /// @param changeEvent Set cellChanged flag? - void changeToVoid(); + void clear(); ///< Change into a void void markCellAsUnchanged(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c8e664791..9acec321f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -164,6 +164,8 @@ namespace MWWorld mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback, resourcePath); mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering, mPhysics)); + mRendering->preloadCommonAssets(); + mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); @@ -303,7 +305,7 @@ namespace MWWorld mProjectileManager->clear(); mLocalScripts.clear(); - mWorldScene->changeToVoid(); + mWorldScene->clear(); mStore.clearDynamic(); mStore.setUp(); @@ -359,9 +361,9 @@ namespace MWWorld mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that // references to custom made records will be recognized + mPlayer->write (writer, progress); mCells.write (writer, progress); mGlobalVariables.write (writer, progress); - mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); mProjectileManager->write (writer, progress); @@ -387,10 +389,13 @@ namespace MWWorld reader.getHNT(mTeleportEnabled, "TELE"); reader.getHNT(mLevitationEnabled, "LEVT"); return; + case ESM::REC_PLAY: + mPlayer->readRecord(reader, type); + mWorldScene->preloadCell(getPlayerPtr().getCell(), true); + break; default: if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && - !mPlayer->readRecord (reader, type) && !mWeatherManager->readRecord (reader, type) && !mCells.readRecord (reader, type, contentFileMap) && !mProjectileManager->readRecord (reader, type) @@ -1468,8 +1473,6 @@ namespace MWWorld } if(player != results.end()) moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); - - mPhysics->debugDraw(); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) @@ -1643,6 +1646,8 @@ namespace MWWorld if (!paused) doPhysics (duration); + mPhysics->debugDraw(); + mWorldScene->update (duration, paused); updateWindowManager (); @@ -2200,6 +2205,8 @@ namespace MWWorld scaleObject(getPlayerPtr(), 1.f); // apply race height + rotateObject(getPlayerPtr(), 0.f, 0.f, 0.f, true); + MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr()); MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr()); @@ -3429,9 +3436,4 @@ namespace MWWorld return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y(); } - void World::preloadCommonAssets() - { - mRendering->preloadCommonAssets(); - } - } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f5e6950e7..c220932a7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -191,8 +191,6 @@ namespace MWWorld virtual void startNewGame (bool bypass); ///< \param bypass Bypass regular game start. - virtual void preloadCommonAssets(); - virtual void clear(); virtual int countSavedGameRecords() const; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 87d0233b5..fd69cdacc 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -51,7 +51,7 @@ namespace ESM esm.writeHNT("VCLR", mColours, 3*LAND_NUM_VERTS); } if (mDataTypes & Land::DATA_VTEX) { - static uint16_t vtex[LAND_NUM_TEXTURES]; + uint16_t vtex[LAND_NUM_TEXTURES]; transposeTextureData(mTextures, vtex); esm.writeHNT("VTEX", vtex, sizeof(vtex)); } @@ -202,7 +202,7 @@ namespace ESM } if (reader.isNextSub("VHGT")) { - static VHGT vhgt; + VHGT vhgt; if (condLoad(reader, flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { @@ -227,7 +227,7 @@ namespace ESM if (reader.isNextSub("VCLR")) condLoad(reader, flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); if (reader.isNextSub("VTEX")) { - static uint16_t vtex[LAND_NUM_TEXTURES]; + uint16_t vtex[LAND_NUM_TEXTURES]; if (condLoad(reader, flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f65e85e53..6fa8e4e49 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -356,12 +356,24 @@ namespace NifOsg osg::ref_ptr textkeys (new TextKeyMapHolder); - osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, 0, false, &textkeys->mTextKeys); + osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { osg::ref_ptr skel = new SceneUtil::Skeleton; - skel->addChild(created); + + osg::Group* root = created->asGroup(); + if (root && root->getDataVariance() == osg::Object::STATIC) + { + skel->setStateSet(root->getStateSet()); + skel->setName(root->getName()); + for (unsigned int i=0; igetNumChildren(); ++i) + skel->addChild(root->getChild(i)); + root->removeChildren(0, root->getNumChildren()); + created = skel; + } + else + skel->addChild(created); created = skel; } @@ -404,15 +416,6 @@ namespace NifOsg toSetup->setFunction(boost::shared_ptr(new ControllerFunction(ctrl))); } - void setupParticleController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int particleflags) - { - bool autoPlay = particleflags & Nif::NiNode::ParticleFlag_AutoPlay; - if (autoPlay) - toSetup->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); - - toSetup->setFunction(boost::shared_ptr(new ControllerFunction(ctrl))); - } - void optimize (const Nif::Node* nifNode, osg::Group* node, bool skipMeshes) { // For nodes with an identity transform, remove the redundant Transform node @@ -547,7 +550,7 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) + std::vector boundTextures, int animflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { osg::ref_ptr node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); @@ -617,10 +620,8 @@ namespace NifOsg } } - if (nifNode->recType == Nif::RC_NiBSAnimationNode) - animflags |= nifNode->flags; - if (nifNode->recType == Nif::RC_NiBSParticleNode) - particleflags |= nifNode->flags; + if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) + animflags = nifNode->flags; // Hide collision shapes, but don't skip the subgraph // We still need to animate the hidden bones so the physics system can access them @@ -663,7 +664,7 @@ namespace NifOsg } if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) - handleParticleSystem(nifNode, node, composite, animflags, particleflags, rootNode); + handleParticleSystem(nifNode, node, composite, animflags, rootNode); if (composite->getNumControllers() > 0) node->addUpdateCallback(composite); @@ -700,7 +701,7 @@ namespace NifOsg for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) - handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode); + handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, skipMeshes, textKeys, rootNode); } } @@ -965,7 +966,7 @@ namespace NifOsg return emitter; } - void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, int particleflags, osg::Node* rootNode) + void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, osg::Node* rootNode) { osg::ref_ptr partsys (new ParticleSystem); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); @@ -986,7 +987,7 @@ namespace NifOsg return; } - osgParticle::ParticleProcessor::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace) + osgParticle::ParticleProcessor::ReferenceFrame rf = (animflags & Nif::NiNode::ParticleFlag_LocalSpace) ? osgParticle::ParticleProcessor::RELATIVE_RF : osgParticle::ParticleProcessor::ABSOLUTE_RF; @@ -1032,10 +1033,10 @@ namespace NifOsg emitterNode->addChild(emitter); osg::ref_ptr callback(new ParticleSystemController(partctrl)); - setupParticleController(partctrl, callback, particleflags); + setupController(partctrl, callback, animflags); emitter->setUpdateCallback(callback); - if (!(particleflags & Nif::NiNode::ParticleFlag_AutoPlay)) + if (!(animflags & Nif::NiNode::ParticleFlag_AutoPlay)) { partsys->setFrozen(true); // HACK: particle system will not render in Frozen state if there was no update @@ -1438,20 +1439,27 @@ namespace NifOsg } const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; - if(tex.texture.empty()) + if(tex.texture.empty() && texprop->controller.empty()) { std::cerr << "Warning: texture layer " << i << " is in use but empty in " << mFilename << std::endl; continue; } - const Nif::NiSourceTexture *st = tex.texture.getPtr(); - osg::ref_ptr image = handleSourceTexture(st, imageManager); + + // create a new texture, will later attempt to share using the SharedStateManager + osg::ref_ptr texture2d; + if (!tex.texture.empty()) + { + const Nif::NiSourceTexture *st = tex.texture.getPtr(); + osg::ref_ptr image = handleSourceTexture(st, imageManager); + texture2d = new osg::Texture2D(image); + } + else + texture2d = new osg::Texture2D; unsigned int clamp = static_cast(tex.clamp); int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; - // create a new texture, will later attempt to share using the SharedStateManager - osg::ref_ptr texture2d (new osg::Texture2D(image)); texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 4392f84c1..50dfbd9a3 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -1,7 +1,6 @@ #include "keyframemanager.hpp" #include -#include #include "objectcache.hpp" diff --git a/components/resource/keyframemanager.hpp b/components/resource/keyframemanager.hpp index 1c2c219bb..ab33d51e3 100644 --- a/components/resource/keyframemanager.hpp +++ b/components/resource/keyframemanager.hpp @@ -4,12 +4,9 @@ #include #include -#include "resourcemanager.hpp" +#include -namespace NifOsg -{ - class KeyframeHolder; -} +#include "resourcemanager.hpp" namespace Resource { diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 28f9a3a62..ee640d6cc 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -13,6 +13,20 @@ namespace SceneUtil { + class LightStateCache + { + public: + osg::Light* lastAppliedLight[8]; + }; + + LightStateCache* getLightStateCache(unsigned int contextid) + { + static std::vector cacheVector; + if (cacheVector.size() < contextid+1) + cacheVector.resize(contextid+1); + return &cacheVector[contextid]; + } + // Resets the modelview matrix to just the view matrix before applying lights. class LightStateAttribute : public osg::StateAttribute { @@ -50,8 +64,17 @@ namespace SceneUtil state.applyModelViewMatrix(state.getInitialViewMatrix()); + LightStateCache* cache = getLightStateCache(state.getContextID()); + for (unsigned int i=0; ilastAppliedLight[i+mIndex]; + if (current != mLights[i].get()) + { + applyLight((GLenum)((int)GL_LIGHT0 + i + mIndex), mLights[i].get()); + cache->lastAppliedLight[i+mIndex] = mLights[i].get(); + } + } state.applyModelViewMatrix(modelViewMatrix); } @@ -262,18 +285,64 @@ namespace SceneUtil return it->second; } + class DisableLight : public osg::StateAttribute + { + public: + DisableLight() : mIndex(0) {} + DisableLight(int index) : mIndex(index) {} + + DisableLight(const DisableLight& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) + : osg::StateAttribute(copy,copyop), mIndex(copy.mIndex) {} + + virtual osg::Object* cloneType() const { return new DisableLight(mIndex); } + virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new DisableLight(*this,copyop); } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual const char* libraryName() const { return "SceneUtil"; } + virtual const char* className() const { return "DisableLight"; } + virtual Type getType() const { return LIGHT; } + + unsigned int getMember() const + { + return mIndex; + } + + virtual bool getModeUsage(ModeUsage & usage) const + { + usage.usesMode(GL_LIGHT0 + mIndex); + return true; + } + + virtual int compare(const StateAttribute &sa) const + { + throw std::runtime_error("DisableLight::compare: unimplemented"); + } + + virtual void apply(osg::State& state) const + { + int lightNum = GL_LIGHT0 + mIndex; + glLightfv( lightNum, GL_AMBIENT, mNull.ptr() ); + glLightfv( lightNum, GL_DIFFUSE, mNull.ptr() ); + glLightfv( lightNum, GL_SPECULAR, mNull.ptr() ); + + LightStateCache* cache = getLightStateCache(state.getContextID()); + cache->lastAppliedLight[mIndex] = NULL; + } + + private: + unsigned int mIndex; + osg::Vec4f mNull; + }; + void LightManager::setStartLight(int start) { mStartLight = start; // Set default light state to zero + // This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling + // we'll have to set a light state that has no visible effect for (int i=start; i<8; ++i) { - osg::ref_ptr defaultLight (new osg::Light(i)); - defaultLight->setAmbient(osg::Vec4()); - defaultLight->setDiffuse(osg::Vec4()); - defaultLight->setSpecular(osg::Vec4()); - defaultLight->setConstantAttenuation(0.f); + osg::ref_ptr defaultLight (new DisableLight(i)); getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF); } } @@ -299,7 +368,7 @@ namespace SceneUtil mId = sLightId++; for (int i=0; i<2; ++i) - mLight[i] = osg::clone(copy.mLight[i].get(), copyop); + mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop); } diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index c8a057263..2c7a6a3d8 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -64,7 +64,7 @@ namespace SceneUtil void setLight(osg::Light* light) { mLight[0] = light; - mLight[1] = osg::clone(light); + mLight[1] = new osg::Light(*light); } /// Get the unique ID for this light source. diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 31d42d342..5a5ecaded 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -15,7 +15,7 @@ namespace SceneUtil for (int i=0; i<2; ++i) // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first // This can be done conveniently in user implementations of the setDefaults() method { - mStateSets[i] = osg::clone(src, osg::CopyOp::SHALLOW_COPY); + mStateSets[i] = new osg::StateSet(*src, osg::CopyOp::SHALLOW_COPY); setDefaults(mStateSets[i]); } } diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index e7b0151dd..1b0e30be9 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -31,6 +31,9 @@ namespace SceneUtil /// Internal use by the WorkQueue. void signalDone(); + /// Set abort flag in order to return from doWork() as soon as possible. May not be respected by all WorkItems. + virtual void abort() {} + protected: OpenThreads::Atomic mDone; OpenThreads::Mutex mMutex; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 041b568df..33fd9186f 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -85,7 +85,7 @@ namespace Shader if (!node.getStateSet()) return node.getOrCreateStateSet(); - osg::ref_ptr newStateSet = osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY); + osg::ref_ptr newStateSet = new osg::StateSet(*node.getStateSet(), osg::CopyOp::SHALLOW_COPY); node.setStateSet(newStateSet); return newStateSet.get(); } @@ -152,7 +152,7 @@ namespace Shader } } - if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL) + if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL && diffuseMap->getImage(0)) { std::string normalMapFileName = diffuseMap->getImage(0)->getFileName(); @@ -194,7 +194,7 @@ namespace Shader mRequirements.back().mNormalHeight = normalHeight; } } - if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL) + if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); @@ -272,6 +272,9 @@ namespace Shader { switch (reqs.mVertexColorMode) { + case GL_AMBIENT: + defineMap["colorMode"] = "3"; + break; default: case GL_AMBIENT_AND_DIFFUSE: defineMap["colorMode"] = "2"; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 03c05260f..0412860ea 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -59,7 +59,7 @@ - + @@ -71,7 +71,7 @@ - + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index 6fa7e92d4..9a6cfd038 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -128,6 +128,18 @@ + + + + + + + + + + + + diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index d62b61b52..7b8486fbe 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -5,7 +5,10 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor) vec3 lightDir; float d; -#if @colorMode == 2 +#if @colorMode == 3 + vec4 diffuse = gl_FrontMaterial.diffuse; + vec3 ambient = vertexColor.xyz; +#elif @colorMode == 2 vec4 diffuse = vertexColor; vec3 ambient = vertexColor.xyz; #else