From 5a939418fc3e48c9ff9f480ccc4848fdb1ee5ae8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Feb 2017 15:49:13 +0100 Subject: [PATCH 01/28] Add missing avcodec_close (Fixes #3741) --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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); From 35bb467c7a385ba754ceb0b3af54aed1832d0c42 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Feb 2017 05:28:06 +0100 Subject: [PATCH 02/28] Fix inverted setting of variable --- apps/openmw/mwphysics/physicssystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; From dfa43af8184bf773d4d46a0f9f0ac4216808a49d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Feb 2017 18:05:24 +0100 Subject: [PATCH 03/28] Update bullet dependency to 2.86 --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d19745ff7..6ba04426f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,11 +254,16 @@ IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) endif() +set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape +if (DEFINED ENV{TRAVIS_BRANCH}) + set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine +endif() + find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) 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) include_directories("." SYSTEM From cdca9b04884a4852fbc287450edcbe372946251b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Feb 2017 20:40:07 +0100 Subject: [PATCH 04/28] Accept bullet 283 on appveyor as well --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ba04426f..be734a8ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,7 +255,7 @@ IF(BOOST_STATIC) endif() set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape -if (DEFINED ENV{TRAVIS_BRANCH}) +if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR}) set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine endif() From 4d4dc1b9c1e08c4a5b258d605118de0b5b15074f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Feb 2017 23:01:36 +0100 Subject: [PATCH 05/28] Add specialized DisableLight state attribute for more efficient undoing of light state Seems to reduce # of GL calls by 10-15% in a typical scene. --- components/sceneutil/lightmanager.cpp | 48 ++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 28f9a3a62..521939170 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -262,18 +262,56 @@ 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) {} + + 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"); + } + + META_StateAttribute(SceneUtil, DisableLight, osg::StateAttribute::LIGHT) + + virtual void apply(osg::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() ); + } + + 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); } } From c00532d82da9524946d729328808893151f7ccd8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Feb 2017 23:36:02 +0100 Subject: [PATCH 06/28] Add LightStateCache to avoid redundantly setting the same gl_Light Normally, osg::State would do this for us (via lastAppliedAttribute), but since we're using a custom StateAttribute to apply all lights at once, we have to track ourselves. Further reduction of GL calls in a typical scene by ~2% --- components/sceneutil/lightmanager.cpp | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 521939170..c8d178152 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); } @@ -289,12 +312,15 @@ namespace SceneUtil META_StateAttribute(SceneUtil, DisableLight, osg::StateAttribute::LIGHT) - virtual void apply(osg::State&) const + 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: From 206e2bf9751fe1e9b6a042119bb57540e47d83ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 04:42:15 +0100 Subject: [PATCH 07/28] Fix camera rotation not being set after save game load (regressed with 1eb338404361dee9a5c987b23836758cbe264f3f) --- apps/openmw/mwworld/worldimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 199cfe6a7..4e43d0268 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,6 +2174,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()); From 45ae8d5ffa778c5a35c3de7037f90d9d3adabb19 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 18:39:24 +0100 Subject: [PATCH 08/28] Add support for GL_AMBIENT colorMode to shaders as required by particle systems --- components/shader/shadervisitor.cpp | 3 +++ files/shaders/lighting.glsl | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 041b568df..799781303 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -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/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 From 249fe9077b7eca151b36c3bdebc2a0052acc932e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 18:50:37 +0100 Subject: [PATCH 09/28] Handle 'tcb' command as an alias for 'tcg' --- apps/openmw/mwrender/renderingmanager.cpp | 8 -------- apps/openmw/mwrender/rendermode.hpp | 1 - apps/openmw/mwscript/miscextensions.cpp | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7bb72ae5d..fb472eb2b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -435,14 +435,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/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index cc64c18c6..88b7e5262 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -235,10 +235,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"); } }; From 43d9f3d5c7b6f4e0665bfbdb4583f7d672346b08 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 18:52:46 +0100 Subject: [PATCH 10/28] Update bullet debug drawer even when the game is paused so the collision mesh will show instantly even when the console is up --- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4e43d0268..babe6baa1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1442,8 +1442,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) @@ -1617,6 +1615,8 @@ namespace MWWorld if (!paused) doPhysics (duration); + mPhysics->debugDraw(); + mWorldScene->update (duration, paused); updateWindowManager (); From 792f505b2a16161756fe0e39fa68a12a2b8950b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 19:16:07 +0100 Subject: [PATCH 11/28] Fix potential crash in ShaderVisitor for textures with no image assigned --- components/shader/shadervisitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 799781303..e603eabff 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -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 + "."); From bccfd6cef8de61cce529972ab739852c26b1c459 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 19:45:31 +0100 Subject: [PATCH 12/28] nifloader: handle textures with no image that are later assigned in a FlipController As found in vurt's trees (Bug #2100) --- components/nifosg/nifloader.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f65e85e53..4c77c1ce4 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1438,20 +1438,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); From 0187f2ce4cc9db208b6be01a0cada2b43278a071 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 19:51:11 +0100 Subject: [PATCH 13/28] nifloader: combine animFlags and particleFlags (Bug #2100) --- components/nifosg/nifloader.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 4c77c1ce4..a51153c3a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -356,7 +356,7 @@ 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()) { @@ -404,15 +404,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 +538,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 +608,8 @@ namespace NifOsg } } - if (nifNode->recType == Nif::RC_NiBSAnimationNode) + if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) animflags |= nifNode->flags; - if (nifNode->recType == Nif::RC_NiBSParticleNode) - particleflags |= 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 +652,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 +689,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 +954,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 +975,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 +1021,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 From 2a42c4781eacfd4e3825b4199e0056a97ebea4ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 19:51:44 +0100 Subject: [PATCH 14/28] nifloader: override animflags instead of accumulating them --- components/nifosg/nifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index a51153c3a..abb9630a5 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -609,7 +609,7 @@ namespace NifOsg } if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) - animflags |= nifNode->flags; + 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 From 4d53ab6c3210f4c2e59927a36461962d703489cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 20:04:17 +0100 Subject: [PATCH 15/28] nifloader: attempt to remove redundant root node when a Skeleton is added --- components/nifosg/nifloader.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index abb9630a5..6fa8e4e49 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -361,7 +361,19 @@ namespace NifOsg 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; } From d1e86d22ca9f2b2a531b712ad0fc271a5b68c1af Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Feb 2017 22:20:57 +0100 Subject: [PATCH 16/28] Check which local map textures actually need to be updated On a typical exterior cell transition, we'll save 3 of 9 map renders. When moving back and forth between 2 cells, we can even reuse 6 of 9. --- apps/openmw/mwrender/localmap.cpp | 40 ++++++++++++++++++++++++++++++- apps/openmw/mwrender/localmap.hpp | 2 ++ 2 files changed, 41 insertions(+), 1 deletion(-) 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; }; From d3e1dbe920a077ad779b5e6746c9d9dfc57d7639 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 00:53:48 +0100 Subject: [PATCH 17/28] Fix race conditions in terrain loader caused by static variables --- components/esm/loadland.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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); } From d62c4259bdca45a8f8208873b24d82290ea201b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 01:07:27 +0100 Subject: [PATCH 18/28] CellPreloader: load the terrain first to match the order in the main thread --- apps/openmw/mwworld/cellpreloader.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index f767c6254..d76e5ddcb 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -79,6 +79,17 @@ namespace MWWorld /// 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) { try @@ -119,17 +130,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: From a46593fa7418caaa078656fd89a565b016f789c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 01:08:07 +0100 Subject: [PATCH 19/28] Add PreloadItem::abort() to avoid no longer required cells from blocking the work thread --- apps/openmw/mwworld/cellpreloader.cpp | 23 +++++++++++++++++++++++ components/sceneutil/workqueue.hpp | 3 +++ 2 files changed, 26 insertions(+) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index d76e5ddcb..3608699fb 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,6 +77,11 @@ namespace MWWorld } } + virtual void abort() + { + mAbort = true; + } + /// Preload work to be called from the worker thread. virtual void doWork() { @@ -92,6 +98,9 @@ namespace MWWorld for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) { + if (mAbort) + break; + try { std::string mesh = *it; @@ -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,7 +263,10 @@ 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); } @@ -257,7 +277,10 @@ namespace MWWorld for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) { if (mPreloadCells.size() >= mMinCacheSize && it->second.mTimeStamp < timestamp - mExpiryDelay) + { + it->second.mWorkItem->abort(); mPreloadCells.erase(it++); + } else ++it; } 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; From c68f662c9aa036beb99f9230db1e7846b0f6330e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 01:24:13 +0100 Subject: [PATCH 20/28] Predict player movement when preloading cells to better handle moving at high speed --- apps/openmw/mwworld/scene.cpp | 34 +++++++++++++++++++++------------- apps/openmw/mwworld/scene.hpp | 10 ++++++---- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c194dcca4..30d021bfd 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -202,9 +202,9 @@ namespace MWWorld if (mPreloadEnabled) { mPreloadTimer += duration; - if (mPreloadTimer > 0.25f) + if (mPreloadTimer > 0.1f) { - preloadCells(); + preloadCells(0.1f); mPreloadTimer = 0.f; } } @@ -461,6 +461,8 @@ namespace MWWorld mechMgr->watchActor(player); MWBase::Environment::get().getWorld()->adjustSky(); + + mLastPlayerPos = pos.asVec3(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) @@ -648,17 +650,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()); @@ -676,11 +685,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) { @@ -703,15 +712,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); @@ -730,6 +737,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) @@ -789,7 +797,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..970a4cad3 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,10 +77,10 @@ namespace MWWorld void getGridCenter(int& cellX, int& cellY); - void preloadCells(); - void preloadTeleportDoorDestinations(); - void preloadExteriorGrid(); - void preloadFastTravelDestinations(); + 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); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); From 839928e2103d501b55bff4c0dd86d42853465fa6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 02:46:30 +0100 Subject: [PATCH 21/28] Fix light data being reset for the wrong light --- components/sceneutil/lightmanager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index c8d178152..3fc15afc0 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -294,6 +294,13 @@ namespace SceneUtil 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; @@ -310,8 +317,6 @@ namespace SceneUtil throw std::runtime_error("DisableLight::compare: unimplemented"); } - META_StateAttribute(SceneUtil, DisableLight, osg::StateAttribute::LIGHT) - virtual void apply(osg::State& state) const { int lightNum = GL_LIGHT0 + mIndex; From 066aa2e60e28db24bdbe34fe23512cf8b326f711 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 02:59:10 +0100 Subject: [PATCH 22/28] Always run preloadCommonAssets even when the menu is skipped Move to before the content files are loaded so we can do preloading in parallel with content file loading --- apps/openmw/engine.cpp | 2 -- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 7 ++----- apps/openmw/mwworld/worldimp.hpp | 2 -- 4 files changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e6511949f..f77130960 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -650,8 +650,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 ee32bc956..bba473834 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/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index babe6baa1..f3d44db39 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -157,6 +157,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(); @@ -3348,9 +3350,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 1fd3ce787..4b86ec14f 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; From d141b98f0cbafbbb5134bbd0d7052bef0692eb72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 03:32:40 +0100 Subject: [PATCH 23/28] Add base animations to preloadCommonAssets --- apps/openmw/mwrender/renderingmanager.cpp | 26 +++++++++++++++++++---- components/resource/keyframemanager.cpp | 1 - components/resource/keyframemanager.hpp | 7 ++---- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fb472eb2b..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); 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 { From 1d8a9ff62243cb6b8e218840d839e6d554f3e8d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 03:47:36 +0100 Subject: [PATCH 24/28] Preload player cell as soon as the player is read from the savegame Giving the worker thread something to do while the rest of the savegame is parsed. --- apps/openmw/mwworld/scene.hpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 970a4cad3..b8cbf49b9 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -82,14 +82,14 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); - void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); - public: Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics); ~Scene(); + void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); + void unloadCell (CellStoreCollection::iterator iter); void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f3d44db39..6468c9b66 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -374,10 +374,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) From 0be86f69bcdc8d423ac29949acaabb6d011387dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 03:48:33 +0100 Subject: [PATCH 25/28] Write the player object first to increase the chance of preloading the player cells in time --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6468c9b66..06e9856e2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -346,9 +346,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); From 3f3d00ffc92644acd0f7d99db5bdb9e5e01f2581 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 04:03:38 +0100 Subject: [PATCH 26/28] Add CellPreloader::clear to avoid potential dangling CellStore pointer and to more aggressively clear preload state from a previous game --- apps/openmw/mwworld/cellpreloader.cpp | 20 +++++++++++++++++++- apps/openmw/mwworld/cellpreloader.hpp | 2 ++ apps/openmw/mwworld/scene.cpp | 4 +++- apps/openmw/mwworld/scene.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 3608699fb..7b90da43b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -272,13 +272,31 @@ namespace MWWorld } } + 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) { - it->second.mWorkItem->abort(); + if (it->second.mWorkItem) + { + it->second.mWorkItem->abort(); + mUnrefQueue->push(it->second.mWorkItem); + } mPreloadCells.erase(it++); } else 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 30d021bfd..3917518a2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -315,13 +315,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) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b8cbf49b9..328538076 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -113,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 06e9856e2..0229f1f93 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -290,7 +290,7 @@ namespace MWWorld mProjectileManager->clear(); mLocalScripts.clear(); - mWorldScene->changeToVoid(); + mWorldScene->clear(); mStore.clearDynamic(); mStore.setUp(); From 93c582064e5ea247373305628a75940d2423b6c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 04:13:13 +0100 Subject: [PATCH 27/28] Remove item shadows for equipped weapon/spell indicators --- files/mygui/openmw_hud.layout | 4 ++-- files/mygui/openmw_resources.xml | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) 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 @@ + + + + + + + + + + + + From 67e4a7e37bec960219b5a8fa1e1f302cd6c9f136 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Feb 2017 04:50:51 +0100 Subject: [PATCH 28/28] Change some osg::clone's to direct copy constructor to avoid dynamic_cast overhead --- apps/openmw/mwrender/animation.cpp | 4 ++-- apps/openmw/mwrender/util.cpp | 2 +- components/sceneutil/lightmanager.cpp | 2 +- components/sceneutil/lightmanager.hpp | 2 +- components/sceneutil/statesetupdater.cpp | 2 +- components/shader/shadervisitor.cpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) 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/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/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 3fc15afc0..ee640d6cc 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -368,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/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index e603eabff..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(); }